Swarm-NG  1.1
swarm::log Namespace Reference

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
 

Detailed Description

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:

void log_event(int evtId, ...)

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:

#define EVT_EJECTTION 1
#define EVT_ENCOUTNER 2
....
dlog.log_event(EVT_ENCOUTNER, bod1, bod2);

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:

dlog.printf("Debug: body %d just hit body %d", bod1, bod2);

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:

int evtref = dlog.log_event(EVT_ENCOUTNER, sys, bod1, bod2, T);
dlog.log_body(ens, sys, bod1, T, evtref);
dlog.log_body(ens, sys, bod2, T, evtref);

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):

#define EVT_MYEVENT 1
int i = 2; double x = 3.; float2 xy = {2., 4.}
dlog.log_event(EVT_MYEVENT, i, x, xy);

when passed an ieventstream object (named 'es'), you'd read it as follows:

switch(es.next())// advances to next event, returning its event ID
{
case EVT_MYEVENT:
int i; double x; float2 xy;
es >> i >> x >> xy;
... do something useful ...
break;
...
}

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).

Function Documentation

struct swarm::log::ALIGN ( )

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

Definition at line 37 of file types.hpp.

References GENERIC, and x.

int swarm::log::lr_extract_sysid ( Db *  secondary,
const Dbt *  key,
const Dbt *  data,
Dbt *  result 
)

Extract the system ID from a log record.

Parameters
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().

template<typename T >
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().