• Antelope Release 5.9 Mac OS X 10.13.2 2019-05-01

 

NAME

EV - BRTT utility for earthquake event view formation

SYNOPSIS


-lbrttutil

#include "EV.h"

DESCRIPTION

There are two fundamental classes, EVServer and EVClient, that implement complete views of earthquake event information from underlying databases. They are intended to be dynamic in response to changing databases. Information from events, origins, origin errors, associations, arrivals, detections, stations, magnitudes and moment tensors are joined in a set of views that can be returned through a set of specialized structures.

The underlying database is monitored and the views are made by a single EVServer object. The views are refreshed automatically by EVServer objects whenever any of the database file modification times have changed. EVServer makes all of the joins though calls to dbmatches(3) only, without using the various other Datascope view generation routines, such as dbjoin(3). Most Datascope view generation routines cannot track dynamic changes in the underlying database. By only using dbmatches(3), which is designed to track certain changes in the underlying database, EVServer objects can track changes in the database and recompute the various view structures as required. All calls to dbmatches(3), dbget(3) or dbgetv(3) made by EVServer objects trap error returns, which could be caused by changes in the database during EVServer processing. When dbget(3), dbgetv(3) or dbmatches(3) return errors, the EVServer object will automatically close the database, reopen it, free all dbmatches(3) hooks, and reform the various views. This will also happen automatically whenever the database files shrink in size.

Once an EVServer object has been created and configured, it will continuously monitor a database and compute the various event views whenever necessary. Interactions with these views are accomplished through EVClient objects. Whereas only one EVServer object should be created for a particular database, any number of EVClient objects can provide independent interfaces to the event view server. Each EVClient object can run safely on separate threads. The EVServer object notifies each of its clients through callback procedures whenever the various views have changed. The EVClient objects can then obtain copies of the changed views from the EVServer object through a set of client methods. All information in the event views are copies of the original views kept be the EVServer object. There are no pointers back to information that could become stale as the views are updated.

EVServer objects can also serve more generic views of various database tables that are not offered in the event views. Specifically, views of wfdisc tables can be requested by a client. In addition views of arrival and detection tables that are not event oriented and not served up as components within event views can be requested by a client. These tables can be monitored by EVServer in the same manner as with the event views with automatic notifications through client callback procedures whenever the tables change. This provides an alternate way to acquire arrival and detection rows that are completely unassociated with any events. Safe EVServer-based versions of generic dbsubset, dbmatches and dbnextid are supported for these tables. As well, EVServer supports safe writing of these tables through EVServer side dbput and dbadd.

EVENT VIEWS

The various event views are returned as c++ structures. The fundamental event view structure, EVEvent, is defined below.


struct EVEvent {
    EVEvent () {
        recno_event = -1;
        evid = -1;
        prefor = -1;
        order = -1;
        lddate_event = 0.0;
        lddate_view = 0.0;
        access_time = 0.0;
        prefor_time = 0.0;
        magnitude = -999.0;
        strcpy (magtype, "");
        pref_origin = -1;
        pref_magnitude_origin = -1;
        pref_moment_origin = -1;
    }

    long recno_event;       /* record number of event row */
    char record_event[154]; /* copy of event row */
    long evid;              /* event row evid value */
    long prefor;            /* event row prefor value */
    long order;             /* order of event row */
    double lddate_event;    /* event row lddate value */
    double lddate_view;     /* largest view lddate value */
    double access_time;     /* last access time */
    double prefor_time;     /* time of preferred origin */

    double magnitude;       /* event preferred magnitude */
    char magtype[8];        /* event preferred magnitude type */

    int pref_origin;        /* preferred origin index */
    int pref_magnitude;     /* preferred magnitude index */
    int pref_moment;        /* preferred moment index */
    int pref_moment_origin; /* preferred moment origin index */
    Table<EVOrigin> origins; /* vector of associated origin views */
};


Each EVEvent structure represents a single row in the database event table along with all of its associated EVOrigin views (i.e. origin relation values with the same evid as the event row). The recno_event structure element is the ordinal record number of the event table. The record_event structure element is a copy of the complete event row. The evid and prefor structure elements are copies of the event row evid and prefor values. The database lddate values are copied into lddate_event, the event row lddate value, and lddate_view, which is the largest of all of the database lddate values from the origin, netmag, and mt database rows associated with the event. All of the associated EVOrigin views are stored in the origins Table.

There is a built-in algorithm for determining a preferred magnitude from the various origin preferred magnitude values and those are reported in magnitude and magtype. The current preferred magnitude algorithm looks at the preferred magnitudes for each origin and choses the one according to the following rules. Magnitudes for each origin are ranked according to importance based on a list of magnitude types and minimum and maximum magnitude ranges. The current ranking is as follows.



preferred_magnitudes &Tbl{
    mww     7.0
    mwp     6.0
    mwc     6.0     8.0
    mwb     4.0     7.0
    mwr     3.0     7.0
    mw      4.0     7.0
    ml      -1.0    5.0
    mb      3.0     6.0
    ms      5.0     7.0
    mww
    mwp
    mwc
    mwb
    mwr
    mw
    ml
    mb
    ms
}


The magnitude type is obtained from the USNEIC reported magnitude types. Magnitude minimum and maximum ranges can follow optionally. The higher in the list, the higher the ranking. The ranking is obtained by taking a particular magnitude type and determining if it is within the magnitude range. If it fails the magnitude range then it will go down to the next entry until a ranking can be found. As an example, a magnitude of type mwp with magnitude 6.5 would have a higher ranking than a magnitude of type mwb with a magnitude of 3.5.

Individual epicenters are returned in EVOrigin structures as defined below.



struct EVOrigin {
    EVOrigin () {
        recno_origin = -1;
        orid = -1;
        lddate_origin = 0.0;
        lddate_origin_view = 0.0;
        time = 0.0;
        preferred = 0;
        magnitude = -999.0;
        strcpy (magtype, "");

        recno_origerr = -1;

        pref_magnitude = -1;
        pref_moment = -1;
    }

    long recno_origin;          /* record number of origin row */
    char record_origin[288];    /* copy of origin row */
    long orid;                  /* origin row orid value */
    double lddate_origin;       /* origin row lddate value */
    double lddate_origin_view;  /* largest view lddate value */
    double time;                /* origin row time value */

    long recno_origerr;         /* record number of origerr row */
    char record_origerr[268];   /* copy of origerr row */

    double magnitude;           /* origin preferred magnitude */
    char magtype[8];            /* origin preferred magnitude type */

    int preferred;              /* is this the preferred origin? */
    Table<EVMagnitude> magnitudes; /* vector of origin magnitude views */
    Table<EVMoment> moments; /* vector of origin moment views */
    Table<EVDetection> detections; /* vector of origin detection views */
    Table<EVAssociation> associations; /* vector of origin associatiln views */
    int pref_magnitude;         /* preferred magnitude magnitudes index */
    int pref_moment;            /* preferred moment moments index */
};


Each EVOrigin structure represents a single row in the database origin table along with all of its associated EVMagnitude, EVMoment, EVDetection and EVAssociation views. The recno_origin structure element is the ordinal record number of the origin table. The record_origin structure element is a copy of the complete origin row. The orid and time structure elements are copies of the origin row orid and time values. The database lddate values are copied into lddate_origin, the origin row lddate value, and lddate_origin_view, which is the largest of all of the database lddate values from the netmag, and mt database rows associated with the origin. If there is an associated origerr row for this origin row, then its record number and a copy of the origerr row are stored in recno_origerr and record_origerr. All of the associated EVMagnitude views are stored in the magnitudes Table. All of the associated EVMoment views are stored in the moments Table. All of the associated EVDetection views are stored in the detections Table. All of the associated EVAssociation views are stored in the associations Table. The EVDetection views return rows from the detection table for which the detection times are approximately equal to the predicted P-arrival times for those stations, whether or not they were promoted to arrival rows in the association process. The preferred magnitude and magnitude type across all magnitudes for this origin are stored in magnitude and magtype using the same ranking described previously.

Event origins can be returned in either abbreviated or expanded forms. In abbreviated forms the information related to associated stations are not returned, which results in empty detections and associations vectors.

Individual network magnitudes are returned in EVMagnitude structures as defined below.



struct EVMagnitude {
    EVMagnitude () {
        recno_netmag = -1;
        lddate_netmag = 0.0;
        magnitude = -999.0;
        strcpy (magtype, "");
    }

    long recno_netmag;          /* record number of netmag row */
    char record_netmag[144];    /* copy of netmag row */
    double lddate_netmag;       /* lddate of netmag row */

    double magnitude;           /* netmag magnitude */
    char magtype[8];            /* netmag magnitude type */

    Table<EVStamag> stamags;    /* Individual station magnitudes */
};


Each EVMagnitude structure represents a single row in the database netmag table.

Individual station magnitude magnitudes are returned in EVStamag structures as defined below.



struct EVStamag {
    EVStamag () {
        recno_stamag = -1;
        lddate_stamag = 0.0;
        arid = -1;
        magnitude = -999.0;
        strcpy (magtype, "");
    }

    long recno_stamag;          /* record number of stamag row */
    char record_stamag[168];    /* copy of stamag row */
    double lddate_stamag;       /* lddate of stamag row */

    long arid;                  /* arid corresponding to this stamag */
    double magnitude;           /* stamag magnitude */
    char magtype[8];            /* stamag magnitude type */
};


Each EVStamag structure represents a single row in the database stamag table.

Individual moment tensors are returned in EVMoment structures as defined below.



struct EVMoment {
    EVMoment () {
        recno_mt = -1;
        lddate_mt = 0.0;
    }

    long recno_mt;          /* record number of mt row */
    char record_mt[564];    /* copy of mt row */
    double lddate_mt;       /* lddate of netmag row */
};


Each EVMoment structure represents a single row in the database mt table.

Individual detections are returned in EVDetection structures as defined below.



struct EVDetection {
    EVDetection () {
        recno_detection = -1;
        lddate_detection = 0.0;
        recno_site = -1;
        presidual = -999.0;
        sresidual = -999.0;
        delta = -999.0;
    }

    long recno_detection;       /* record number of detection row */
    char record_detection[204]; /* copy of detection row */
    double lddate_detection;    /* lddate of detection row */

    long recno_site;            /* record number of site row */
    char record_site[202];      /* copy of site row */

    double presidual;           /* P-arrival time residual */
    double sresidual;           /* S-arrival time residual */
    double delta;               /* origin-site distance */
};


Each EVDetection structure represents a single row in the database detection table. Also, the associated station site row is stored in recno_site and record_site. When a detection is associated with an origin, the origin to site distance is stored in delta, the residual time relative to the predicted P-arrival time is stored in presidual and the residual time relative to the predicted S-arrival time is stored in sresidual.

Individual arrival associations are returned in EVAssociation structures as defined below.



struct EVAssociation {
    EVAssociation () {
        recno_assoc = -1;
        lddate_assoc = 0.0;

        recno_arrival = -1;
        lddate_arrival = 0.0;

        recno_site = -1;

        defining = 0;
    }

    long recno_assoc;           /* record number of assoc row */
    char record_assoc[190];     /* copy of assoc row */
    double lddate_assoc;        /* lddate of assoc row */

    long recno_arrival;         /* record number of arrival row */
    char record_arrival[274];   /* copy of arrival row */
    double lddate_arrival;      /* lddate of arrival row */

    long recno_site;            /* record number of site row */
    char record_site[202];      /* copy of site row */

    int defining;               /* Is this arrival defining? */
};


Each EVAssociation structure represents a single row in the database assoc table. Also, the associated arrival arrival row is stored in recno_arrival and record_arrival and the associated station site row is stored in recno_site and record_site.

EVSERVER PROCESSING

The EVServer objects keep up-to-date and complete lists of all database events as a Table of EVEvent structures. In order to save memory, the complete events lists are always composed of abbreviated versions of the EVOrigin structures. The complete events vector can be querried by each of the EVClient objects as desired. These querries return copies of the events vector so that client-side processing will not be effected by underlying database changes.

In addition to the events list maintained by EVServer objects, a list of recent detections, within the last hour relative to system time or relative to the most recent event time, is also maintained by EVServer objects as a Table of EVDetection structures. These vectors can also be querried by client objects and returned as copies. Note that the most recent detections list contains detections that have not been associated with origins and therefore the delta and residual values are set to NULL values.

In addition to these lists, EVServer objects keep track of a set of particular events as "expanded" events which are composed of EVEvent structures with expanded versions of the EVOrigin structures that contain all of the detection and association information. Clients can request expanded events by specifying their evid values. Also, EVServer objects always keep track of the most recently modified event and automatically promotes that event into the expanded event list.

By subclassing the EVClient class and defining the virtual responseCallback method, client objects can be notified by the EVServer objects whenever the underlying database changed drastically and had to be closed and re-opened, or the complete events list has changed, or the detections list has changed, or any of the expanded events have changed, or the most recently modified event has changed.

EVSERVER CONSTRUCTOR

EVServer(std::string dbname, int age_ref_now = 1, int max_events = -1, int check_nfs = 1)

With the following constructor arguments:

When the object is created the refreshEvents method is called once to initialize the internal views.

EVSERVER METHODS

EVCLIENT CONSTRUCTOR

EVClient(EVServer *ev)

With the following constructor arguments:

EVCLIENT METHODS

Whenever a single Datascope complete record string is at the end of the expression it can be follow by {<dbexpr>}. When this is done the regular Datascope expression <dbexpr> is evaluated against the record.

  • int getValue (Table<EVOrigin> *origins, std::string query, int *type, void *value)
    This is like the EVEvent list method except it applies to lists of EVOrigin structures. When using this method the query string should start with origins.

The following methods apply to the manipulation of sorting tab lists that can be used to implement indirect referencing into lists of EVEvent and EVOrigin structures. These provide high performance methods for rearranging event and origin lists to, for instance, display sorted lists in spreadsheet applications.

  • static void setEventsTabs(EVEventsTab *view, Table<int> *sort_tab=NULL)
    This static method will set a previously allocated EVEventsTab structure according to a sort_tab list which should contain the sorted indexes from a particular list of EVEvent structures. If sort_tab is NULL, then the sorting index reference values are the same as the index values. EVEventsTab structure should be created and initialized by the following constructors.
    • EVEventsTab (EVClient *evclient)
      This will create an
      EvEVentsTab structure and initialize it with a fresh copy of the EVEvent list from an EVClient object. The EVServer events lock is not used in this constructor so the application code should lock and release the server events lock as needed.
    • EVEventsTab (std::shared_ptr<Table<EVEvent>> ptr)
      In this version of the constructor a shared pointer, ptr
      to a list of EVEvent structures has already been determined and is used to initialize the EVEventsTab structure.
  • static void setOriginsTabs(EVOriginsTab *view, Table<int> *sort_tab=NULL)
    This static method will set a previously allocated
    EVOriginsTab structure according to a sort_tab list which should contain the sorted indexes from a particular list of EVOrigin structures. If sort_tab is NULL, then the sorting index reference values are the same as the index values. EVOriginsTab structure should be created and initialized by the following constructors.
    • EVOriginsTab (Table<EVOrigin> *origins)
      This will create an
      EVOriginsTab structure and initialize it with the EVOrigin list from origins.
  • void sort (EVEventsTab *view, std::string query, int reverse)
  • void sort (EVOriginsTab *view, std::string query, int reverse)
    These
    EVClient object methods will sort lists of EVEvent structures or EVOrigin structures, defined within the EVEventsTab and EVOriginsTab structures, using the string query evaluation expression. If reverse is non-zero, then the sort order is reversed. The EVEventsTab and EVOriginsTab structures must be previously allocated and initialized using the constructors defined previously. The syntax of query is as defined previously with the getValue methods, except that the initial events[<index>]. or origins[<index>]. should be omitted since the list indexing is manipulated automatically during the sort processing. Note that the expression value types will determine how the sort is conducted; i.e. numerical types will sort using numerical comparisons and string types will sort using string comparisons. The results of the sorting are returned within the EVEventsTab and EVOriginsTab structures.
  • Table<int> sort (Table<EVEvent> *events, std::string query, int reverse)
  • Table<int> sort (Table<EVOrigin> *origins, std::string query, int reverse)
    This is a second form of the sorting methods defined previously. In this form the lists of
    EVEvent or EVOrigin structures is input directly, through events or origins, and the sorted indexes into the input lists are returned as tables of integers. The returned tables of integers could then be used by the setEventsTabs and setOriginsTabs static methods to set the sort order into EVEventsTab and EVOriginsTab structures.
  • static int getRow (EVEventsTab *view, int index)
  • static int getRow (EVOriginsTab *view, int index)
    These static methods will return the row corresponding to the list index index
    for sorted EVEvent and EVOrigin lists. By "index" we mean the index within the original unsorted lists and by "row" we mean the sorted order of that index, for instance a row in a sorted spreadsheet.
  • static int getIndex (EVEventsTab *view, int row)
  • static int getIndex (EVOriginsTab *view, int row)
    These static methods will return the index corresponding to the sorted row row
    for sorted EVEvent and EVOrigin lists. By "index" we mean the index within the original unsorted lists and by "row" we mean the sorted order of that index, for instance a row in a sorted spreadsheet.
  • static int findEvidRow (EVEventsTab *view, long evid)
    This static method will return the sorted row corresponding to the
    EVEvent structure with evid evid.
  • static int findEvidIndex (EVEventsTab *view, long evid)
    This static method will return the original list index corresponding to the
    EVEvent structure with evid evid.
  • static int findOridRow (EVOriginsTab *view, long orid)
    This static method will return the sorted row corresponding to the
    EVOrigin structure with orid orid.
  • static int findOridIndex (EVOriginsTab *view, long orid)
    This static method will return the original list index corresponding to the
    EVOrigin structure with orid orid.

EXAMPLE

Following is a simple c++ example code that will monitor a database. The event view structures are printed out when the EVServer
first opens and reads the database and then differences are printed out whenever the database changes (this requires setting the refresh_interval program argument to a positive number).


/* A test for the EV classes. */

#include "EV.h"

class MyEVClient : public EVClient {
public:
    MyEVClient (EVServer *ev) : EVClient (ev) {}

    Table<EVEvent> events;
    Table<EVDetection> detections;
    EVEvent last_modified_event;
    EVEvent most_recent_event;

protected:
    void responseCallback (EVResponse response);
};

void MyEVClient::responseCallback (EVResponse response) {
    printf ("MyEVClient::processEVCallback");
    if (response.database_reset) printf (" database_reset");
    if (response.events_changed) printf (" events_changed");
    if (response.detections_changed) printf (" detections_changed");
    if (response.last_modified_event_changed) printf (" last_modified_event_changed");
    if (response.most_recent_event_changed) printf (" most_recent_event_changed");
    printf ("\n");

    Table<EVEvent> events_tmp;
    Table<EVDetection> detections_tmp;
    EVEvent last_modified_event_tmp;
    EVEvent most_recent_event_tmp;

    grabEventsLock();
    if (response.events_changed) {
        events_tmp = getEvents();
    }
    if (response.detections_changed) {
        detections_tmp = getDetections();
    }
    if (response.last_modified_event_changed) {
        getLastModifiedEvent (last_modified_event_tmp);
    }
    if (response.most_recent_event_changed) {
        getMostRecentEvent (most_recent_event_tmp);
    }
    releaseEventsLock();

    if (response.events_changed) {
        printf ("events changes:\n");
        EVServer::print (&events, &events_tmp);
        events = events_tmp;
    }
    if (response.detections_changed) {
        printf ("detections changes:\n");
        EVServer::print (&detections, &detections_tmp);
        detections = detections_tmp;
    }
    if (response.last_modified_event_changed) {
        printf ("last_modified_event changes:\n");
        EVServer::print (&last_modified_event, &last_modified_event_tmp);
        last_modified_event = last_modified_event_tmp;
    }
    if (response.most_recent_event_changed) {
        printf ("most_recent_event changes:\n");
        EVServer::print (&most_recent_event, &most_recent_event_tmp);
        most_recent_event = most_recent_event_tmp;
    }
}

int
main (int argc, char **argv)

{
    if (argc != 1 && argc != 2 && argc != 3) {
        fprintf (stderr, "usage:ev_test [dbname [refresh_interval]]\n");
        exit (1);
    }

    char *dbname;
    if (argc == 1) {
        dbname = (char *) "/opt/antelope/data/db/demo/demo";
    } else {
        dbname = argv[1];
    }
    double interval = 0.0;
    if (argc == 3) interval = atof(argv[2]);

    EVServer *ev;
    if (interval > 0.0) {
        ev = new EVServer ( dbname );
    } else {
        ev = new EVServer ( dbname, 0 );
    }

    MyEVClient *evcl = new MyEVClient ( ev );

    evcl->grabEventsLock();

    evcl->events = evcl->getEvents();
    evcl->detections = evcl->getDetections();
    evcl->getLastModifiedEvent (evcl->last_modified_event);
    evcl->getMostRecentEvent (evcl->most_recent_event);

    evcl->releaseEventsLock();

    printf ("Events:\n");
    EVServer::print (&(evcl->events), "    ");
    printf ("\nDetections:\n");
    EVServer::print (&(evcl->detections), "    ");
    printf ("\nLast Modified Event:\n");
    EVServer::print (&(evcl->last_modified_event));
    printf ("\nMost Recent Event:\n");
    EVServer::print (&(evcl->most_recent_event));

    if (interval <= 0.0) exit (0);

    ev->autoRefreshEvents ( interval );
    printf ("refreshing every %f seconds\n", interval);

    pause();

    exit (0);
}



BUGS AND CAVEATS

In this implementation of the EV classes, all of the objects are in the same address space. In the future we plan on expanding this so that the server and clients can be in different address spaces (processes) linked by ORB-based or TCP/IP based communications links. Because the current implementation puts all servers and clients in the same address space and because Datascope is not thread safe and it shares resources for a single database across all database opens within the same address space, it is dangerous for clients to try any direct database accesses to the database managed by the event view servers. Client threads should not use any Datascope routines other than dbgetv(3) and dbex_eval(3) and only those routines with the temporary database pointers returned by the various server getDbptr methods.

SEE ALSO

Table(3)

AUTHOR

Danny Harvey, BRTT