While the flat parameter space provided by CEvent is
usable for small experiments, it can pose problems for larger experiments, or
experiments involving multiple detector systems:
It can be hard to keep track of paramter indices.
Parameter indices are not as meaningful as names.
When combining several pieces of experimental apparatus, it can be hard to ensure there are no index conflicts.
The TreeParameter package, first introduced and developed by Daniel Bazin addresses these issues. TreeParameter was a contributed package in SpecTcl versions prior to 3.0, and now is a fully supported package. There are differences between the original TreeParameter package and the supported package. This documentation will describe the supported version.
Tree parameter provides the following:
Classes that associated C++ objects with elements of a CEvent.
This association is based on parameter name, and allows the user to compose a
parallel structure in which these objects live.
A similar set of classes that provides mappings to Tcl variables. This allows your computation to be very easily parameterized, or steered with Tcl scripts.
A set of SpecTcl commands for manipulating and viewing the attributes of tree variables and tree parameters.
A graphical user interface that understands the tree parameter hierarchy and supports the creation of spectra, as well as parameter and variable manipulations, and gate creation.
This section describes the classes that TreeParameter exports to you. The interfaces to these classes match and extend the original classes written by Daniel Bazin, however the implementation is completely new, includes additional entries and functionality not present in the original code.
TreeParameter exports classes that sit between you and the
CEvent
object, as well as classes that make access to Tcl variables simple.
Each of these interface sets exports classes that support array as well as
individual item access.
The definitions of all four of these classes can be imported into your program
by:
#include <TreeParameter.h>
We will now describe the four exported classes:
CEvent
The main features of the class header are shown in the example below.
Before discussing the member functions in this header, I want to point out that
other member functions not shown allow a CTreeParameter
object to be treated as if it were a variable of type double.
Thus CTreeParameters can appear on the left or right side of
assignments, and in expressions wherever a double can occur.
Problems that existed in this treatment prior to SpecTcl 3.0 which required you to
use temporary double variables in some expressions have been
eliminated.
Example 3-18. Important Features of the CTreeParameter class definition.
class CTreeParameter
{
public:
static const std::string TreeParameterVersion; //
static void BindParameters(); //
static void setEvent(CEvent& rEvent); //
CTreeParameter();
CTreeParameter(std::string name);
CTreeParameter(std::string name, std::string units);
CTreeParameter(std::string name, double lowLimit, double highLimit,
std::string units);
CTreeParameter(std::string name, UInt_t channels,
double lowLimit, double highLimit, std::string units);
CTreeParameter(std::string name, UInt_t resolution); //
CTreeParameter(std::string name, UInt_t resolution,
double lowLimit, double widthOrHigh,
std::string units, bool widthOrHighGiven);
CTreeParameter(std::string name, const CTreeParameter& Template);
CTreeParameter(const CTreeParameter& rhs);
void Initialize(std::string name, UInt_t resolution);
void Initialize(std::string name, UInt_t resolution,
double lowLimit, double highOrWidth, std::string units,
bool highOrWidthGiven); //
void Initialize(std::string name);
void Initialize(std::string name, std::string units);
void Initialize(std::string name, UInt_t channels,
double lowLimit, double highLimit, std::string units);
bool isBound(); //
bool isValid(); //
void setInvalid(); //
void Bind(); //
};
In the list below, the numbers refer to the numbers in the example.

TreeParameterVersion
is a static member
function that returns the version of the tree parameter software you
are using. It can be used to conditionalize code that depends on
features of the software that may vary as it continues to be developed.
To call static functions you precede them with their classname
followed by a pair of colons e.g.:
string version = CTreeParameter::TreeParameterVersion();

BindParameters does this by iterating through
the set of CTreeParameter objects that have been
created and calling each parameter's Bind member.
The SpecTcl-3.0 version of the TreeParameter package (version 1.3),
does not ordinarily require you to call this function. Versions prior
to this version did require you to explicitly call this function after
you had created all of your tree parameters. There are unusual
circumstances that may require you to call this function if you are
dynamically creating CTreeParameter objects.

CTreeParameter object must
be made to correspond to a given element of the
CEvent passed in to the event processor. Since
SpecTcl uses a pool of
CEvent objects, this correspondence must be
established on an event by event basis. setEvent
makes the
correspondence between elements of rEvent and
each known CTreeParameter object.
With SpecTcl 3.0, (tree parameter version 1.3) and later, this is done automatically. For previous versions, this had to be called in the first event processor e.g. It is harmless to call this more than once per event. The call is not very expensive, as all it does it to save a pointer to the event.

CTreeParameter
object. Each tree parameter has associated with it a name, units of measure,
and recommendations for spectrum axis specifications. Depending on what
the underlying paramter represents, any of the various constructors may
be more natural to you. The parameters to these constructors are:
Name: name
Type: std::string
Meaning: A name given to the parameter. This name has several
purposes. At bind time, it determines which SpecTcl parameter
the CTreeParameter object will be
bound to. The name also determines the place of the
parameter in the parameter structure hierarchy. A parameter
can be though of, like a filename, as composed of path
elements. In the TreeParameter gui, these path elements
can be navigated, and determine which parameters are placed
close to each other on the GUI. The convention is to
separate path elements with periods just like C/C++ structure
elements references are.
Name: units
Type: std::string
Meaning: The units of measure of the parameter. The units of measure are used in the axis titles of spectra that are made on these parameters. No checks are made to ensure that units make physical sense. That's your responsibility.
Name: lowLimit
Type: double
Meaning: Associates a suggested low limit for axes of spectra created on this parameter. When parameters are known to have a fixed meaningful range (as is usually the case), this should be the lower limit of that range. For example, for a raw digitizer value, this will typically be 0.0.
Name: highLimit
Type: double
Meaning: Associates a suggested high limit for axes of spectra created on this parameter. When parameters are known to have a fixed meaningful range, this should be the upper limit of the range. For example, for a raw 12 bit digitizer, this should be 4095.0.
Name: channels
Type: UInt_t
Meaning: Suggested number of channels for a spectrum axis on this parameter. Where the resolution of a parameters is known, this should reflect that. For example, for a raw 12 bit digitizer, this should be 4096.
Name: resolution
Type: Uint_t
Meaning: Another way to specify the range, and resolution of a parameter. This is the 'number of bits' that contain useful data in a parameter that has an underlying integer representation. For example, for our 12 bit adc, this would be 12.
Name: widthOrHighGiven
Type: bool
Meaning: Some construtors have a parameter named
widthOrHigh if
widthOrHighGiven is true,
widthOrHigh is a channel width,
otherwise it is a high limit on the parameter.
Name: Template
Type: CTreeParameter&
Meaning: A template from which the tree parameter will be created.
The only difference in definition between the new
CTreeParameter and
Template is the name.

CTreeParameter objects that are created
with e.g. the default constructor, or for those whose definition
must be changed, the Initialize member can be
called. Note that if the name of a tree parameter is changed and that
parmeter was bound it must be bound again.
The parameters to Initialize are the same as for
the constructors (see above), however the parameter
highOrWidth has a different name indicating that
it is logically inverted relative to the constructor parameter
widthOrHigh. This is an inconsistency that was
present in the initial Tree parameter package and retained for
the sake of compatibility..


isValid member of the
underlying CEvent& event array.

CEvent&
that correpsonds to this
CTreeParameter. After this call,
isValid will return false,
referencing the parameter in a way that causes its value to be taken
will throw a std::string exception with the
text:
Attempted getValue of unset ValidValue object until
the parameter is assigned a value.

If a parameter already exists by that name, this object is made to correspond to it.
If a parameter does not yet exist by this name, a new one is created.
It is possible and legal to have two CTreeParameter
objects that are bound to the same SpecTcl parameter.
While this was possible in versions prior to 1.3, it was
not done in such a way as to give a consistent picture
of the parameter value in all objects.
Many detector systems consist in part or whole of a set of identical channels.
These are conveniently represented as arrays. The class that creates
arrays of CTreeParameter
is CTreeParameterArray.
The key pieces of the class definition of CTreeParameterArray
is shown in the example below.
Example 3-19. CTreeParameterArray class interface
class CTreeParameterArray
{
public:
CTreeParameterArray();
CTreeParameterArray(STD(string) baseName,
UInt_t resolution, UInt_t numElements, Int_t baseIndex);
CTreeParameterArray(STD(string) baseName,
UInt_t resolution,
double lowLimit, double highOrWidth,
STD(string) units, bool widthOrHighGiven,
UInt_t elements, Int_t firstIndex);
CTreeParameterArray(STD(string) baseName, UInt_t elements, Int_t baseIndex); //
CTreeParameterArray(STD(string) baseName, STD(string) units,
UInt_t elements, Int_t firstIndex);
CTreeParameterArray(STD(string) baseName,
double low, double high, STD(string) units,
UInt_t elements, Int_t firstIndex);
CTreeParameterArray(STD(string) baseName, UInt_t channels,
double low, double high, STD(string)units,
UInt_t elements, Int_t firstIndex);
~CTreeParameterArray();
void Initialize(STD(string) baseName, UInt_t resolution,
UInt_t elements, Int_t baseIndex);
void Initialize(STD(string) baseName, UInt_t resolution,
double lowLimit, double widthOrHeight,
STD(string) units, bool widthOrHeightGiven,
UInt_t elements, Int_t firstIndex);
void Initialize(STD(string) baseName, UInt_t elements, Int_t firstIndex); //
void Initialize(STD(string) baseName, STD(string) units, UInt_t elements,
Int_t firstIndex);
void Initialize(STD(string) baseName, double lowLimit, double highLimit,
STD(string) units, UInt_t elements, Int_t firstIndex);
void Initialize(STD(string) baseName, UInt_t channels,
double lowLimit, double highLimit, STD(string) units,
UInt_t elements, Int_t firstIndex);
UInt_t size(); //
Int_t lowIndex(); //
CTreeParameter& operator[](Int_t nIndex); //
};
Once more the numbers in the list below refer to the callout numbers in the example above.

CTreeParameter objects.
For the most part, the parameters for these constructors
are the same as those for the
CTreeParameter
constructors. There are, however two additional parameters:
Name: elements
Type: Uint_t
Meaning: Number of elements in the array.
Name: firstIndex
Type: Int_t
Meaning: The index of the first element. This parameter allows the array to have any starting integer index.



firstIndex parameter of
the constructor or initializer.

firstIndex. If the index supplieds is not valid
for this object a CRangeError exception will
be thrown. The return value of this function can be used directly
as a CTreeParameter which, in turn, can be treated like a double.
for example if tpa below is a
CTreeParameterArray the code fragments below
are valid uses:
double value = tpa[3];
tp = 2.0*tpa[0]; // tp a CTreeParameter.
tpa[3] = tpa[0] + tpa[1];
and so on.
Two classes CTreeVariable and
CTreeVariableArray
support access to variables managed by the treevariable
command (described below). In addition to these, the
CTCLVariable class is a SpecTcl class
that provides access to
Tcl variables. TreeVariables, on the other create a namespace of
variables that is:
Hierarchical in nature
Maps to a parallel hierarchcal structure in the C/C++ code.
Associates units of measure with each variable
Provides a new command treevariable for setting and querying tree variables
A typical use of Tcl variables that are accessed via CTreeVariable
objects is to parameterize computations performed by the event processors in the
event processing pipeline. Later in this chapter we will present an example that
uses tree variables to implement a weighted sum of parameters where the weight
is set by Tcl variables that can be adjusted at run time.
Now lets start looking at the key parts of the
CTreeVariable
class definition:
Example 3-20. The CTreeVariable class definition
class CTreeVariable
{
public:
static void BindVariables(CTCLInterpreter& rInterp); //
public:
CTreeVariable();
CTreeVariable(std::string name, double value, std::string units); //
void Initialize(std::string name, double value, std::string units); //
void Bind(); //
std::string getName(); //
double getValue(); //
std::string getUnit(); //
bool hasChanged(); //
bool valueChanged(); //
void resetChanged(); //
};
As with
CTreeParameter, CTreeVariable
has member functions not shown above that allow its objects to be treated
as if they were double variables.
This allows a CTreeVariable object to be used in
arbitrary calculations involving actual simple variables,
CTreeParameter objects, or any other object that
can be treated as a number.
The discussion below provides some detail about specific marked sections of the example.

CTreeVariable objects and underlying tree variables.
The connection can be a many to one connection (that is several
CTreeVariables
can represent the same underlying tree variable
(if their names are the same).
This differs from the functionality of versions prior to 1.3
where bad things would happen if you did this accidentally or
intentionally.
Prior to TreeParam 1.3 it was necessary for you to call this function to set up these bindings. In Treeparam 1.3 and SpecTcl 3.0 and later, this function is called for you and, unless you are dynamically creating bindings after SpecTcl is initialized, you won't have to make this call.

CTreeVariableobjects.
If you use the default (unparameterized) constructor you will
need to later call Initialize to set up the
variable meaning.
Constructor parameters are:
Name: name
Type: std::string
Meaning: The name of the Tcl variable to which this
CTreeVariable
object will be bound. Names of tree variables,
as with Parameters, are considered to be made up of
path elements that are separated by periods.
Note that this causes some problems for Tcl scripts that
manipulate these variables. It is necessary to use
curly brackets to do variable substitution e.g.:
${a.b.c}.
Name: value
Type: double
Meaning: Provides an initial value for the Tcl variable.
Name: units
Type: std::string
Meaning: Provides units of measure to be associated with the variable. The units are just an uninterpreted string, nothing is done to ensure that the units make any physical sense.

CTreeVariable constructed using
the default constructor to be set up.

CTreeVariable
object to the underlying treevariable. If no Tcl variable exists,
one is created and initialized to the initial value
specified in the constructor or Initialize call.
Several
CTreeVariable objects can be bound to the
same named treevariable. The only strangeness is that if the units
string differs amongst them, the most recently bound object will
set the units of the underlying variable.

CTreeVariable
object, returns its name.

CTreeVariable
object, returns its current value. This function is usually not
necessary.
CTreeVariable
objects can be treated as double variables.
The original CTreeVariable class defined this function,
therefore we provide it in version 1.3 and later for compatibility
with any software that may use it.




In this section we will take the unpacker we wrote for our CAEN V775 TDC and modify it to use the Tree parameter package. We will assume that:
This unpacker will later be used in a larger experiment
The ADC is a detector system at the NSCL called test
Our unpacker might be re-used later in a different piece of apparatus.
Before we create tree parameters, we need to decide on some
naming conventions. We will have the event processor create
a CTreeParameterArray for the parameters it
unpacks. The array name will be test.times
Since we want our event processor to be re-usable, we will break this
name up into a prefix and the times string.
The prefix will be a parameter of the event processor constructor.
Below is the modified class header for our event processor:
Example 3-21. Event processor header using tree parameter
#ifndef __MYEVENTPROCESSOR_H #define __MYEVENTPROCESSOR_H #include <EventProcessor.h> #include <TreeParameter.h> //class MyEventProcessor : public CEventProcessor { private: unsigned short m_id; unsigned short m_slot; CTreeParameterArray m_parameters; //
public: MyEventProcessor(unsigned short id, unsigned short slot, std::string prefix); //
virtual Bool_t operator()(const Address_t pEvent, CEvent& rEvent, CAnalyzer& rAnalyzer, CBufferDecoder& rDecoder); }; #endif
The changed sections are marked:


CTreeParameterArray into which we will
unpack the data from our packet.

prefix to the
constructor. This string will have the path elements prior to the
times part of the path. The constructor will join
these path elements together.
Our modification to MyEventProcessor.h imply that we
will need to change the constructor of the event processor as well as making
modifications to the operator() member function.
The example below shows the modified constructor:
Example 3-22. MyEventProcessor's constructor.
MyEventProcessor::MyEventProcessor(unsigned short id, unsigned short slot, string prefix) : //m_id(id), m_slot(slot) //
{ string arrayName = prefix; arrayName += '.'; //
arrayName += "times"; m_parameters.Initialize(arrayName, 4096, 0.0, 4095.0, //
string("channels"), 32, 0); }


CTreeParameterArray m_parameters
to use it's default constructor. This is because we need to
build up the name of the parameter from the prefix and the
times string. The constructor will use one of the
CTreeParameterArray::Initialize
functions to actually initialize the tree parameter array.


arrayName
are the spectrum axis resolution hints, 4096 channels in the range 0 through
4095. The next two parameters indicate that the array should have 32 elements
and that the first index is 0.
We must also modify the unpacking function
MyEventProcessor::operator()
so that the data are unpacked to the tree parameter array, rather than
some rEvent member. This is done by changing exactly one line of the
implementation of that function.
The example below shows the TDC unpacking section of that function's implementation. The modified line is set off in this font.
Example 3-23. Changes to
MyEventProcessoroperator()
to use Tree parameter.
Int_t channelCount = (headerLow & COUNTMASK) >> COUNTSHIFT;
do {
UShort_t dataHigh = *body;
UShort_t dataLow = body[1];
body +=2;
if( (dataHigh & TYPEMASK) != TYPE_DATA) break;
UShort_t channel = dataHigh & CHANMASK;
UShort_t conversion = dataLow & DATAMASK;
if ((!(dataLow & UNDERFLOWBIT)) && (!(dataLow & OVERFLOWBIT))) {
m_parameters[channel] = conversion;
}
channelCount--;
} while (channelCount >= 0);
Last and certainly not least, we need to modify how the event processor is
constructed in
CMySpecTclApp::CreateAnalysisPipeline:
Our setup scripts contains spectrum, and parameter definitions. The parameter definitions are taken care of by the tree parameter objects. Our spectrum definitions may get made at a time prior to the parameter definitions. With TreeParameter, it is therefore customary to read in definition files using hte Tree Parameter GUI after SpecTcl has started. In this section, we will see how to create these spectrum definitions using the GUI.
We still want our button to attach online. Therefore, before we start up SpecTcl, delete the parameter and spectrum definitions so that we are left with the following setup file:
proc online host {
set url tcp://$host:2602/
catch stop
attach -pipe /usr/opt/daq/current/bin/spectcldaq $url
start
}
button .onl -text {Attach online} -command [list online thechad]
pack .onl
Now lets startup SpecTcl and start playing with the GUI. In the rest of this section we will:
Create a 1-d spectrum for each element of the tree parameter array.
Create a 2-d spectrum of the first time vs. the second time.
Save these definitions as a setup file.
Restart SpecTcl and load the setup file we created.
Analyze some data to be sure that everything works correctly.
![]() | The treeparameter GUI for SpecTcl 3.0 and earlier will behave in unpredictable ways if you have:
|
The Tree parameter GUI for SpecTcl 3.0 is shown in the figure below:
This Gui is essentially unmodified from Daniel Bazin's original Tree parameter GUI from SpecTcl 2.x and earlier. SpecTcl 3.1 features a brand new GUI which will be described in the reference material. Since at the time this book is being written, the majority of the users are using the 3.0 and earlier tree paramter gui, I thought it best to describe that software.The tree parameter gui allows us to create an array for each element of a tree parameter array. The example below describes how to create this array of spectra for our times.
Example 3-25. Creating an array of spectra with the TreeParam gui
Creating an array of spectra
Check the array checkbutton in the buff colored strip of controls.
Type a base name for the spectra in the Spectrum Name text field. We'll choose test.times, so that the spectrum names will match the parameter names.
Get any of the tree parameter array element names in the Parameter field at the left side of the salmon colored strip of controls. You can do this either by typing it in, or by clicking on the Parameter label above the text entry. Clicking the Parameter label will pop up a tree of cascading menus that reflects the tree parameter hierarchy.
When you have followed this procedure the top part of the GUI should look like the figure below:
To finish creating the spectra click the button. This will generate the spectra test.times.00, test.times.01 ... test.times.31.To Generate the 2-d spectrum of test.times.00 vs. test.times.01:
Example 3-26. Using the TreeParam gui to create a 2-d spectrum.
Generating a 2-d Spectrum.
Check the 2d radio button in the Spectrum type controls box in the upper left of the cyan part of the GUI
If necessary, select the X parameter as before. If you have not done anything since creating the 1-d spectrum, test.times.00 should still be in the X parameter entry>
Set the Y parameter to test.times.01
In order to keep this spectrum small, adjust the bins on both the X and Y parameters to be 256.
Click the button to create the spectrum.
Now that we have our spectra defined, lets save the definitions to file so that the next time we run SpecTcl we can retrieve these definitions.
Let's see how spectrum definition files are intended to work. Exit SpecTcl, and restart it. As we should expect, the tree parameter GUI is empty of spectra.
Example 3-28. Loading a definition file
Loading definition files with the GUI
Click the button.
In the file chooser that pops up, select or type in myspectra.tcl
Click the button.
The Tree parameter gui spectrum window is now filled with the same spectrum definitions we had prior to exiting SpecTcl. A few notes are in order:
Not only Spectrum defintions are saved. Gate definitions, gate applications and the values of tree variables that are modified from their initial values in your C++ code are also saved.
If you make modifications using the Tree parameter Gui, a file named failsafe.tcl will be created after each modification. If you forget to save your definitions or for some reason, SpecTcl crashes, loading this file will, in general, get you back to where you were the last time you ran SpecTcl.
Using the checkbutton determines whether or not the tree parameter GUI will destroy all your definitions prior to loading the file or attempt to merge the file definitions with any that might have already been made.
To test our work:
Run SpecTcl and read in our configuration file.
Display the spectrum test.times.00 in Xamine.
Click the button to attach SpecTcl to the online system.
Run your packetized Readout program and start a run.
The CTreeVariable class transparently maps objects that
operate exactly like doubles into variables that can be
manipulated within Tcl scripts. These variables are not Tcl variables.
This mapping makes it easy to control computations you do in event processors.
Suppose, for example, you want to create a parameter that
is the weighted sum of test.times.00 and test.times.01 but you want to adjust the
weighting of the two parameters at run time.
To do this we will:
Modify the event processor written in the previous section to make its tree parameter array accessible from other objects. (If you are an experienced object oriented programmer don't scream yet, later we'll show how to dispense with this).
Write an event processor that can take a reference to a MyEventUnpacker as a construction parameter, a tree variable base name, and an output event name and compute weighted sums of the first two parameters in the MyEventUnpacker into a tree parameter whose name is given by the output event parameter using weights based on the tree variable base name.
The example below shows the only modification we need to do to MyEventUnpacker.
This modification is done in the file MyEventUnpacker.h.
Example 3-29. Making MyEventUnpacker's tree parameters public
#ifndef __MYEVENTPROCESSOR_H
#define __MYEVENTPROCESSOR_H
#include <EventProcessor.h>
#include <TreeParameter.h>
class MyEventProcessor : public CEventProcessor
{
private:
unsigned short m_id;
unsigned short m_slot;
public: // <- added this line.
CTreeParameterArray m_parameters;
public:
MyEventProcessor(unsigned short id,
unsigned short slot,
std::string prefix);
virtual Bool_t operator()(const Address_t pEvent,
CEvent& rEvent,
CAnalyzer& rAnalyzer,
CBufferDecoder& rDecoder);
};
#endif
The example below is the header for our weighted sum event processor:
Example 3-30. First try at a weighted sum computer
#ifndef __WSUM_H
#define __WSUM_H
#include <EventProcessor.h>
#include <TreeParameter.h>
class MyEventProcessor;
class wsum : public CEventProcessor
{
MyEventProcessor& m_source; //
CTreeVariable m_weight1; //
CTreeVariable m_weight2;
CTreeParameter m_output; //
public:
wsum(MyEventProcessor& source, std::string variableBase,
std::string outputName);
virtual Bool_t operator()(const Address_t pEvent,
CEvent& rEvent,
CAnalyzer& rAnalyzer,
CBufferDecoder& rDecoder);
};
#endif

m_source will store a reference to the event processor
that has the tree array we will compute the sum from. Since we made
MyEventUnpacker::m_parameters
public, the reference will allow us to get those parameter values.


Now lets look at the implementation:
Note how both treeparameters and tree variables can be treated as if they were just double precision numbers. Note as well how we check to determine that both parameters our computation depends on have actually been defined by this event.Finally you need to:
Modify Makefile so that it compiles and links in wsum.cpp
Modify MySpecTclApp.cpp to include the wsum.h headerr.
Modify CMySpecTclApp::CreateAnalysisPipeline
to read as shown below:
void
CMySpecTclApp::CreateAnalysisPipeline(CAnalyzer& rAnalyzer)
{
MyEventProcessor* ep = new MyEventProcessor(0x8100, 10, "test");
RegisterEventProcessor(*ep, "CaenRaw");
RegisterEventProcessor(*(new wsum(*ep, "weights", "computed.sum")), "sum");
}
Click on the tab of the tree parameter gui. See below for an image of the GUI after you do that.
The radio button allows you to select a slot on the GUI. The lable is actually a button that brings up a pull down navigator of the variable name hierarchy. The buttons reload the current value and units of the variable (or array if the array checkbox is set) into the selected line, while the button sets the values/units of the variable or array to the the values inthe Value and Unit column.
Use the to put the
weights.w1 and weights.w2 variables
into the top two lines of the gui. We can test if our software works by:
Setting the value of weights.w1 to 1 and
weights.w2 to 0. If we then take data on the
sum spectrum it should be identical to the test.times.00 spectrum.
If we next set the value of weights.w1 to 0
and that of weights.w2 to 1, clear the spectra
and take data, the sum spectrum should be identical to the test.times.01
spectrum.
Perform these tests, be sure to click the button after you change each variable.
In the previous section, we built an event processor that computed a weighted sum. The weights were determined by tree variables. Our implementation, while demonstrating how to use tree variables has several problems:
We had to tightly bind the class to the MyEventProcessor
class, a true weighted sum computation event processor should be able to
operate on an arbitrary pair of parameters.
We had to make the m_parameters member variable of
MyEventProcessor public to allow access to the
variables. This seriously breaks class encapsulation and can lead to
maintenance headeaches. For exmple, suppose that we find that the
overhead of treeparameters is too large to tolerate, and reimplement
MyEventProcessor to operate directly on
rEvent instead, eliminating tree parameter from its member data,
that will break the wsum class.
What if we want to operate on elements other than 0 or 1 of the array? again the summer should be able to operate on any pair of parameters, so that it can be re-used as needed.
The creation of a
CTreeParameter or CTreeVariable
object registers it in an STL multimap. It's not terribly important to know
what an STL multimap is, only that it is an efficiently searchable data structure.
To search for a CTreeParameter object by name, you can call:
static std::multimap<std::string, CTreeParameter*>::iterator
CTreeParameter::find(std::string name)
But what is this strange looking return type? An iterator is a pointer like object.
It can be dereferenced and incremented. Incrementing a pointer makes it point to
the next object in the collection (a multimap in this case). Iterators generally
run from the first element to a value that 'points' past the last element, called
end. If CTreeParameter::find
fails to find the parameter you are looking for it will return the end iterator.
CTreeParameter::end returns the
value of the end iterator.
The iterator for a multimap 'points' to a pair of items called first
and second. The first item is the name of the tree parameter.
The second name is a pointer to a tree parameter object that has that name.
For example:
CTreeParameter* pParam;
std::multimap<std::string, CTreeParameter*>::iterator p =
CTreeParameter::find("test.times.00");
if (p == CTreeParameter::end) {
// could not find the parameter... error handling goes here.
...
}
else {
pParam = p->second;
}
Locates the tree parameter with the name test.times.00.
Similarly, Tree variables have:
static std::multimap<std::string, CTreeVariable*>::iterator
CTreeVariable::find(std::string name)
and
static std::multimap<std::string, CTreeVariable*>::iterator CTreeVariable::end();
So our strategy will be to pass the constructor of wsum, names of parameters and weight variables. The wsum OnAttach function will then locate the appropriate parameters and variables, save pointers to them and use them in our computations.
The example below shows the new header for wsum:
Example 3-32. Revised wsum header
#ifndef __WSUM_H
#define __WSUM_H
#include <EventProcessor.h>
#include <TreeParameter.h>
class MyEventProcessor;
class wsum : public CEventProcessor
{
private:
std::string m_param1Name;
std::string m_param2Name;
//
std::string m_weight1Name;
std::string m_weight2Name;
CTreeParameter* m_param1;
CTreeParameter* m_param2;
//
CTreeVariable* m_weight1;
CTreeVariable* m_weight2;
public:
wsum(std::string param1, std::string param2,
std::string weight1, std::string weight2); //
virtual Bool_t OnAttach(CAnalyzer& rAnalyzer); //
virtual Bool_t operator()(const Address_t pEvent,
CEvent& rEvent,
CAnalyzer& rAnalyzer,
CBufferDecoder& rDecoder);
};
#endif




OnAttach member is called when the
event processor is actually attached to the analysis framework.
It's not too important to know exactly what this means, only that by
that time, the objects our event processors are searching for will
have been constructed and the mechanisms to search for them in place.
We will leave as an exercise for the reader the operator()
implementation. Just remember that a pointer to a CTreeParmater
or CTreeVariable object are just like a
double*
below are the constructor for this version of wsum:
Example 3-33. Revised constructor for wsum
wsum::(string param1, string param2, string weight1, string weight2) :
m_param1Name(param1),
m_param2Name(param2),
m_weight1Name(weight1),
m_weight2Name(weight2)
{}
We know how to implement the OnAttach function from our description of the
find() and related member functions. It is a bad error
for the names to not be able to lookup objects so if that happens, we will
emit an error to stderr and return false which will
abort the remainder of the initialization.
Example 3-34. The OnAttach member of the modified wsum
Bool_t
wsum::OnAttach(CAnalyzer& rAnalyzer)
{
// Look up the tree parameters.
multimap<string, CTreeParameter*>::iterator p;
p = CTreeParameter::find(m_param1Name);
if (p == CTreeParameter::end()) {
cerr << "wsum::OnAttach find failed for " << m_param1Name << endl;
return kfFALSE;
}
m_param1 = p->second;
p = CTreeParameter::find(m_param2Name);
if (p == CTreeParameter::end()) {
cerr << "wsum::OnAttach find failed for " << m_param2Name << endl;
return kfFALSE;
}
m_param2 = p->second;
// Now do the weights:
multimap<string, CTreeParameter*>::iterator pw;
pw = CTreeVariable::find(m_weight1Name);
if (pw == CTreeVariable::end()) {
cerr << "wsum::OnAttach find failed for " << m_weight1Name << endl;
return kfFALSE;
}
m_pweight1 = pw->second;
pw = CTreeVariable::find(m_weight2Name);
if (pw == CTreeVariable::end()) {
cerr << "wsum::OnAttach find failed for " << m_weight2Name << endl;
return kfFALSE;
}
m_pweight2 = pw->second;
}