Chapter 5. The SpecTcl event sink pipeline.

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


#include <EventSink.h>
class EventCounter : public CEventSink
  unsigned m_nEvents;
  unsigned m_nParametrs;
  unsigned m_nPeriod;
  EventCounter(unsigned period);

  void operator()(CEventList& rEvents);


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


Let's now look at operator();

Example 5-3. EventCounter::operator() implemntation

EventCounter::operator()(CEventList& rEvents)
  for (auto p = rEvents.begin(); p != rEvents.end(); p++) {
    CEvent* pEvent = *p;
    m_nParameters += pEvent->getDopeVector().size();

    if((m_nEvents % m_nPeriod) == 0) {
      std::cout << "Processed " << m_nEvents
                <<" for a total of " <<m_nParameters << " parameters\n";;