If the event processing pipeline takes the raw event and produces a set of parameters, the event sink pipeline operates on those parameters. The most important event sink pipeline element is the hisotogrammer. That's the part of SpecTcl that increments histograms depending on the definitions in the parameter, spectrum and gate dictionaries as well as the application of gates t spectra.
Filters are another important class of event sink pipeline element. Each filter is appended to the event sink pipeline and allowed to process event lists to produce filtered event files.
It is also possible to add processors to the even sink pipeline.
Processors in the event sink pipeline are subclassed from
CEventSink
defined in
EventSink.h. They can override the following
virtual methods:
virtual void OnAttach( CAnalyzer& rAnalyzer);
Called when the element is added to the event sink pipeline.
rAnalyzer
is a reference to the
SpecTcl analyzer. The analyzer controls the flow of
analysis through SpecTcl. Note that in most cases, this
is actually an instance of a
CTCLAnalyzer
.
virtual void OnDetach( CAnalyzer& rAnalyzer);
Called if an element is removed from the event sink pipeline
(It's not unusual for elements to remain in the pipeline
for the duration of the program run). Once more
rAnalyzer
is a reference to the
analyzer object that controls the analysis of data by
SpecTcl.
virtual = 0 void operator()( CEventList& rEvents);
This method is pure virtual in the base class and therefore must be implemented in the specific classes that get instantiated. It is called for each burst of events from the event processing pipeline.
CEventList
is defined in
EventList.h. You can either get the
underlying vector of CEvent
pointers
or iterate through it using its iterator interface.
Let's look at an example event sink processor. The processor will be pretty simple. It's going to maintain, and periodically output, the number of events processed and the total number of parameters that have been given values.
To do this efficiently, we're going to need to delve into the nasty
corners of the CEvent
class. Specifically,
while, for each event, we can find out how many parameters
are defined by iterating that object and asking each parameter
if its defined, for large analysis cases this can be very inefficent.
The fact that this iteration can be so inefficient brings us to an optimization that SpecTcl uses to ensure that the histogrammer neither has to iterate over the parameters in an event, nor iterate over the histograms that have been defined. This optimization is incredibly good for sparse analysis.
Sparse analysis means that for a large analysis case, large number of parameters, and/or large number of histograms, for any event, we really are only going to assign values to a small percentage of parameters and have to increment a very small subset of the histograms.
SpecTcl's CEvent
helps SpecTcl to iterate over
the sparse parameter space by building, as each parameter is defined,
a DopeVector. A dope vector is just a vector
whose elements are indices to those elements that have been defined.
Using the dope vector makes our life simple; The number of elements
in a CEvent
object's dope vector is the
number of parameters that have been given values for that event.
This background out of the way; lets look at the class header first:
Example 5-1. Event sink processor header
#ifndef EVENTCOUNTER_H #define EVENTCOUNTER_H #include <EventSink.h> class EventCounter : public CEventSink { private: unsigned m_nEvents; unsigned m_nParametrs; unsigned m_nPeriod; public: EventCounter(unsigned period); void operator()(CEventList& rEvents); }; #endif
The purpose of the m_nPeriod
and
period
attributes and variables are to
define how often we output statistics.
m_nEvents
will count events and
m_nParameters
will total the parameters set across
all events.
The constructor is pretty simple:
Example 5-2. EventCounter constructor implementation
#include "EventCounter.h" #include <Event.h> #include <EventList.h> #include <iostream> EventCounter::EventCounter(unsigned period) : m_nEvents(0), m_nParameters(0), m_nPeriod(period) {}
Let's now look at operator()
;
Example 5-3. EventCounter::operator()
implemntation
void EventCounter::operator()(CEventList& rEvents) { for (auto p = rEvents.begin(); p != rEvents.end(); p++) { CEvent* pEvent = *p; m_nEvents++; m_nParameters += pEvent->getDopeVector().size(); if((m_nEvents % m_nPeriod) == 0) { std::cout << "Processed " << m_nEvents <<" for a total of " <<m_nParameters << " parameters\n";; } } }