Swarm-NG
1.1
|
The event/data logging system for swarm More...
Classes | |
class | bdb_writer |
Writer plugin to output the log into a Berkeley-DB database. More... | |
class | binary_writer |
A writer plugin that writes to binary files. More... | |
class | event_record |
Define event_record class. More... | |
class | event_record<-1 > |
Specialized version for NumData=-1 to allow for variable length. More... | |
class | event_record< 0 > |
Specialized version for NumData=0 to allow for no double data (is this needed?) More... | |
class | host_array_writer |
A writer plugin that keeps the data in the memory. More... | |
class | manager |
Manage CPU/GPU logs and writing them to appropriate output. More... | |
class | null_writer |
A writer plugin to use when the log output is not needed. More... | |
struct | body_set |
body_set class: hold a set of indices to bodies in a given system in a given ensemble. More... | |
class | writer |
Abstract output writer interface. More... | |
Functions | |
template<typename T > | |
void | put_in_dbt (const T &t, Dbt *data) |
Helper function to put any constant size type into a Dbt struct. More... | |
int | lr_extract_sysid (Db *secondary, const Dbt *key, const Dbt *data, Dbt *result) |
Extract the system ID from a log record. More... | |
template<typename T > | |
int | bdb_compare (DB *db, const DBT *k1, const DBT *k2) |
Make a comparison function for a C++ type T that supports "<" operator. | |
int | num_ints_for_event (const int code) |
Helper function to decide how many integers are expected. | |
int | num_doubles_for_event (const int code) |
Helper function to decide how many doubles are expected. | |
template<typename L > | |
GENERIC void | system (L &l, ensemble::SystemRefConst sys) |
Store a snapshot of the entire system (EVT_SNAPSHOT). | |
template<typename L > | |
GENERIC void | system (L &l, const ensemble &ens, const int sys) |
Store a snapshot of the entire system (EVT_SNAPSHOT). | |
template<typename L > | |
GENERIC void | ensemble (L &l, const swarm::ensemble &ens) |
Store a snapshot of the entire ensemble (convenience). | |
template<typename L > | |
GENERIC void | ensemble_enabled (L &l, const swarm::ensemble &ens) |
Store a snapshot of all non-disabled systems within the ensemble (convenience). | |
struct | ALIGN (8) body |
for on-GPU state logging of bodies NOTE: I've written out the datatypes explicitly, because of alignment requirements that have to be hand-tuned between the device and host code. More... | |
GENERIC const body_set< 1 > | make_body_set (const ensemble &ens, int sys, int bod0) |
For one body case. | |
GENERIC const body_set< 2 > | make_body_set (const ensemble &ens, int sys, int bod0, int bod1) |
For two body case. | |
GENERIC const body_set< 3 > | make_body_set (const ensemble &ens, int sys, int bod0, int bod1, int bod2) |
For three body case. | |
Variables | |
writer_plugin_initializer < bdb_writer > | bdb_writer_plugin ("bdb","This is the Berkeley DB writer") |
Initialize the database writer plugin. | |
writer_plugin_initializer < binary_writer > | binary_writer_plugin ("binary","This is the binary writer") |
Initialize the binary writer plugin. | |
static const int | EVT_SNAPSHOT = 1 |
marks a snapshot of a system. see swarm::log::system() down below | |
static const int | EVT_EJECTION = 2 |
marks a body has been ejected | |
static const int | EVT_ENCOUNTER = 3 |
marks near a close encounter event | |
static const int | EVT_COLLISION = 4 |
marks near a physical collision | |
static const int | EVT_COLLISION_CENTRAL = 5 |
marks near a collision with central body | |
static const int | EVT_RV_OBS = 11 |
marks near a transit of planet in front of star | |
static const int | EVT_ASTROM_OBS = 12 |
marks near a transit of planet in front of star | |
static const int | EVT_TIMING_OBS = 13 |
marks near a transit of planet in front of star | |
static const int | EVT_DIRECT_IMAGE_OBS = 14 |
marks near a transit of planet in front of star | |
static const int | EVT_TRANSIT = 15 |
marks near a transit of planet in front of star | |
static const int | EVT_OCCULTATION = 16 |
marks near an occultation of star in front of planet | |
static const int | EVT_MUTUAL_EVENT = 17 |
marks near a mutual event, planet in front of planet | |
static const int | EVT_FIRST_OBS_CODE = EVT_RV_OBS |
marks begining of event codes used for observations | |
static const int | EVT_LAST_OBS_CODE = EVT_MUTUAL_EVENT |
marks end of event codes used for observations | |
The event/data logging system for swarm
Mario Juric
Despite the horrendously complicated implementation in log.hpp, the ideas behind it are quite simple:
There are two "event log buffer" objects to which the user can log events, or the current states of systems being integrated: hlog and dlog. They should be viewed as two facets of the same object: hlog the facet exposed towards the CPU code, while dlog is to be used by the GPU code. Otherwise, their interfaces are nearly identical. Currently, these are global objects, both on CPU and GPU (somewhat like cout in C++)
There are two types of messages you can send to these log buffers. They differ in that they get stored to their individual "sub-buffers", each being optimized for the message of a given type:
1) Bodies - using log_body(ens, sys, bod, T) you can store the current (m,x,v,T) coordinates of a body bod in system sys, plus an integer worth of arbitrary user data. Look at eventlog_base::body structure to see exactly what is stored. This is the basic function with which snapshots of full system state can be built (see docs/snapshotting.txt).
2) Events - these are general purpose records, not longer than ~200 bytes, that begin with an integer eventId and then store whatever data you need to store. Use log_event() set of templated functions to log events. Their effective interface behaves like:
where ... stands for up to 9 variables of arbitrary types (think printf from C). The machinery beneath these functions will byte-copy the value of all arguments into the event buffer. As the name suggests, use them to record events.
Example usage:
2a) Printf- an implementation of C printf() functionality, callable from the GPU. Internally, these are just events of event ID EVT_PRINTF, but they also have a set of convenience template functions that makes the interface look exactly like that of printf(). On the CPU side, there's some code to recognize EVT_PRINTF events, reconstruct them and pass to C's printf() to produce the output string. GPU interface example:
where bod1, bod2 are integers.
From the integrator-developer's point of view, these functions allow an easy way to record that something has happened in the simulation, and to store a (sub)set of bodies related to that event of interest. These events and accompanying data will be marshalled to output files for subsequent data analysis. For example, let's say you want to record a collision event between bodies bod1 and bod2. You might do it like this:
The above will store a user-defined EVT_ENCOUTNER event to the event log, recording the system id, the ids of the two bodies involved, and the time of the collision. log_event returns a unique integer (evtref). Subsequent two lines stored all the information about the bodies (masses, position, velocities..), together with the event reference (evtref) which can be used in the subsequent analysis to match these two entries in the outputs with the reason why they're there.
For now, the event buffers cannot be read directly but are automatically "piped" to a "writer" object. A writer is an object that inherits from abstract class writer (see swarm.h), and is attached to hlog using hlog's attach_sink() function. writer implements 'void process(ieventstream&)' method that when called should drain the buffers of all events (usually storing them to a file, or outputting some to the screen (e.g., the printfs)). This method is called whenever the event buffer is flushed (more below).
The argument, an ieventstream object, provides an istream-like interface to the eventlog. Example: if you stored the following event (e.g., either in GPU code, or CPU code, doesn't matter):
when passed an ieventstream object (named 'es'), you'd read it as follows:
This is only for reading events. For accessing the sub-buffer holding the bodies, an array-like interface is provided instead.
Sample implementation (but still very useful) of a writer is class binary_writer, in swarmlog.cpp. binary_writer just binary-dumps the bodies and events sub-buffer into two files, while printf-ing any EVT_PRINTF events to the screen.
Both the CPU and GPU buffers have limited reserved space (which is why I refer to them as buffers). Periodically, they have to be flushed; otherwise they'll start drooping events/bodies.
Flushing is accomplished by calling hlog.flush() (immediately), or via hlog.flush_if_needed() (that will flush() only if buffers are near capacity). There's no explicit call to flush the GPU buffer – hlog.flush* family will take care of that.
Best practice: call flush_if_needed() after every kernel call (for GPU kernels), or after every step (CPU kernels).
Finally, before they can be used the eventlog buffers must be initialized by calling hlog.initialize(), and a sink must be attached via hlog.attach_sink(). See swarm.cpp for an example. Also, immediately before launching a kernel from which you'll use dlog, you MUST call hlog.prepare_for_gpu(). Failure to do so will result in a rupture in space-time continuum and death of countless kittens. And you don't want to be a kitten-killer.
Post-final notes: The implementation is rather dirty at this point, as I didn't have the time to make it prettier. The design has passed through a few redesigns, and there may be vestiges of the old code here and there (so if something seems wrong or illogical, that may be the reason). For the same reason, a few variable names may be misnomers. There are also likely to be many, many, many bugs. But the code has been confirmed to work both on CPU and a GPU (a GTX 260).
struct swarm::log::ALIGN | ( | 8 | ) |
for on-GPU state logging of bodies NOTE: I've written out the datatypes explicitly, because of alignment requirements that have to be hand-tuned between the device and host code.
Yes, this is unfortunate. NOTE: put all doubles first, to avoid interstitial padding and alignment nvcc vs. gcc issues
load body information from ensemble to body structure
int swarm::log::lr_extract_sysid | ( | Db * | secondary, |
const Dbt * | key, | ||
const Dbt * | data, | ||
Dbt * | result | ||
) |
Extract the system ID from a log record.
secondary,: | The secondary parameter is the database handle for the secondary. |
key | : The key parameter is a Dbt referencing the primary key. |
data | : The data parameter is a Dbt referencing the primary data item. |
result | : The result parameter is a zeroed Dbt in which the callback function should fill in data and size fields that describe the secondary key or keys. |
Definition at line 51 of file bdb_database.cpp.
References put_in_dbt().
void swarm::log::put_in_dbt | ( | const T & | t, |
Dbt * | data | ||
) |
Helper function to put any constant size type into a Dbt struct.
the flag DB_DBT_APPMALLOC hints berkeley db that the data is allocated by the application
Definition at line 35 of file bdb_database.cpp.
Referenced by lr_extract_sysid().