Parallel SpecTcl (version 6.0-dev) | ||
---|---|---|
Prev | Chapter 3. How to create your thread-safe analysis pipeline | Next |
Example 3-6. Obtaining the Parallel SpecTcl skeleton
mkdir parallel cd parallel cp /usr/opt/spectcl/6.0-000/DDASSkel/* .
The skeleton consists of several files. The example has been created following the model of analysis pipeline of the one existing experiments.
Makefile: the user needs to add the *.o of the event processor that he created.
MySpecTclapp: class to create and register the event processor(s) you need in the order you want them called. NB global static definitions are forbidden (more later).
MyParameterMapper: this file doesn't need to be edited.
MyParameters: class that contains the main data structure. In the example provided it contains several nested structures (ChannelData, CTreeParameter, MyPipelineData, MyParameters2). In Figure 3-1, a schematic representation of the structure is showed.
MyCalibrator: class that contains the user-defined event processor. NB: the structure of the class is thread-safe any modification may cause into segmentation faults and/or memory leaks.
In editing MySpecTclApp.cpp, you need to consider three things:
The actual event processing pipeline you are going to create. Note that unlike SpecTcl, all pipeline elements (event processors) must have names.
The name of the shared object the Makefile will create.
To setup the event processing pipeline you'll need to provide #include directives to pull in the headers for your event processors. Don't specify absolute paths, take care of that in the Makefile.
Here's an example
Example 3-7. Including event processor headers
... // Here you should include your headers for your event processors. #include <MyCalibrator.h> ...
The command shown is in the skeleton to indicate where to put these #include directives.
Next, locate the class MySpecTclApp
, create and register the event processor(s) you need
in the order you want them called. For example,
Example 3-8. Registering MyParameters, MyParameterMapper, and the processing pipeline
void CMySpecTclApp::CreateAnalysisPipeline(CAnalyzer& rAnalyzer) { MyParameters* pParams = new MyParameters("ddas"); MyParameterMapper* pMapper = new MyParameterMapper(*pParams); RegisterData(pMapper); RegisterEventProcessor(*(new DAQ::DDAS::CDDASBuiltUnpacker({1, 2, 3 })), "Raw"); RegisterEventProcessor(*(new MyCalibrator()), "Cal"); }
It is fundamental to notice how objects are declared and dynamically created. Global static object are forbidden by multithreading programming. Local static object are good. Declaring any of those objects without initialization outside CMySpecTclApp::CreateAnalysisPipeline would cause undefined behaviors.
Let's look now at the MyParameters definition.
Example 3-9. Definition of MyParameters.h
#ifndef MYPARAMETERS_H #define MYPARAMETERS_H #include <config.h> #include <TreeParameter.h> #include <string> #include <PipelineData.h> #include <vector> #include "MyPipelineData.h" #include "MyParameters2.h" struct ChannelData { CTreeParameter energy; CTreeParameter timestamp; // Initialize the TreeParameters // // We will create TreeParameters with names associated with // the name passed in. For example, if name = "rawdata", then // we will create TreeParameters with names rawdata.energy and // rawdata.timestamp. // // \param name name of parent in tree structure ChannelData(); ChannelData(const ChannelData& rhs); void Initialize(std::string name); void Reset(); }; //____________________________________________________________ // Struct for top-level events // struct MyParameters { ChannelData chan[1000]; CTreeParameter multiplicity; MyPipelineData data; MyParameters2 example; // Ctor MyParameters(std::string name); // Dtor ~MyParameters(){}; // Copy Ctor MyParameters(const MyParameters& rhs); void Reset(); }; #endif
The core of your analysis pipeline is your MyCalibrator. Let's look at it more in details:
Example 3-10. Definition of MyCalibrator.h
#ifndef __MYCALIBRATOR_H #define __MYCALIBRATOR_H #include <EventProcessor.h> #include <TranslatorPointer.h> #include <TCLAnalyzer.h> class MyParameterMapper; class MyCalibrator : public CEventProcessor { public: MyParameterMapper* m_pParameterMapper; MyCalibrator(); MyCalibrator(const MyCalibrator& rhs); ~MyCalibrator(); virtual MyCalibrator* clone() { return new MyCalibrator(*this); } void setParameterMapper(DAQ::DDAS::CParameterMapper& rParameterMapper); virtual Bool_t operator()(const Address_t pEvent, CEvent& rEvent, CAnalyzer& rAnalyzer, CBufferDecoder& rDecoder, BufferTranslator& trans, long thread); }; #endif
For the implementation of MyCalibrator class:
Example 3-12. Definition of MyCalibrator.cpp
#include <ThreadAnalyzer.h> #include "MyCalibrator.h" #include "MyParameterMapper.h" #include "MyParameters.h" #include <ZMQRDPatternClass.h> MyCalibrator::MyCalibrator() {} MyCalibrator::MyCalibrator(const MyCalibrator& rhs) {} MyCalibrator::~MyCalibrator() { delete m_pParameterMapper; } void MyCalibrator::setParameterMapper(DAQ::DDAS::CParameterMapper& rParameterMapper) { m_pParameterMapper = reinterpret_cast<MyParameterMapper*>(&rParameterMapper); } Bool_t MyCalibrator::operator()(const Address_t pEvent, CEvent& rEvent, CAnalyzer& rAnalyzer, CBufferDecoder& rDecoder, BufferTranslator& trans, long thread) { auto& params = m_pParameterMapper->m_params; // loop over hits for(int i= 0; i<params.data.m_chanHit.size(); i++){ int id = params.data.m_chanHit[i]; double ran = ( static_cast<double>(rand())) / (static_cast<double>(RAND_MAX)); if( id == 341) { if (rEvent[params.chan[id].energy.getId()].isValid()){ params.example.ex1.energy = (params.chan[id].energy + ran) + 0.0; params.example.ex1.ecal = params.example.ex1.energy*params.example.var.var1.c1; } if (rEvent[params.chan[id].timestamp.getId()].isValid()) params.example.ex1.time = params.chan[id].timestamp + 10.0; } } return kfTRUE; };
Once you've edited everything, use make to build the shared object and don't forget to add it to the Makefile.