3.4. Using Tree Parameter

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:

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:

3.4.1. The classes of TreeParameter

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:

3.4.1.1. Classes Supporting access to 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; // (1)
  static void BindParameters();	                 // (2)
  static void setEvent(CEvent& rEvent);	         // (3)

 
  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);  // (4)
  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);              // (5)
  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();                                     // (6)
  bool   isValid();                                   // (7)
  void   setInvalid();                                // (8)
  void   Bind();                                      // (9)
  
 
};





                

In the list below, the numbers refer to the numbers in the example.

(1)
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();
                            
(2)
Tree parameters must at some point be bound to the underlying SpecTcl parameters. The static member function 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.

(3)
At run time, each 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.

(4)
This set of functions constructs a 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.

(5)
For 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..

(6)
Returns true if the parameter is already bound.
(7)
Invokes the isValid member of the underlying CEvent& event array.
(8)
Calling this invalidates the element of the 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.
(9)
Binds this parameter to the SpecTcl parameter namespace.

  • 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); // (1)
  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); // (2)
  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();		// (3)
  Int_t lowIndex();		// (4)
  CTreeParameter& operator[](Int_t nIndex); // (5)
    
  
};




                    

Once more the numbers in the list below refer to the callout numbers in the example above.

(1)
These are various constructors for arrays of 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.

(2)
Allows you to modify the definition of an array or, alternatively initialize one that was constructed using the default constructor (the one with no parameters). The parameters are the same as for the constructor.
(3)
Returns the number of elements in the array.
(4)
Returns the index of the first element of the array. This is the value specified by the firstIndex parameter of the constructor or initializer.
(5)
Returns a reference to the specified element of the array. Note that the first index may not be 0 if the array was constructed with a nonzero 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.

3.4.1.2. Classes supporting access to Tcl variables

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); // (1)

public:
  CTreeVariable();
  CTreeVariable(std::string name, double value, std::string units); // (2)

  void Initialize(std::string name, double value, std::string units); // (3)

  void Bind();			// (4)
  std::string getName();	// (5)
  double getValue();		// (6)
  std::string getUnit();	// (7)
  bool hasChanged();		// (8)
  bool valueChanged();		// (9)
  void resetChanged();		// (10)
  
};

                    

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.

(1)
This function makes the connection between all 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.

(2)
Constructors for 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.

(3)
Allows a CTreeVariable constructed using the default constructor to be set up.
(4)
Binds this 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.

(5)
Given a CTreeVariable object, returns its name.
(6)
Given a 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.
(7)
Returns the unit string associated with this variable.
(8)
Returns true if the object's properties have changed. Properties are defined to be the name or units of the variable.
(9)
Returns true if the object's value changed.
(10)
Resets both changed flags to false.

3.4.2. Modifying our event unpacker to use Tree Parameter

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:

These assumption will guide some of the choices we make throughout the implementaton of this software.

3.4.2.1. MyEventProcessor header for tree parameters.

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>              // (1)

class MyEventProcessor : public CEventProcessor 
{
private:
  unsigned short       m_id;	
  unsigned short       m_slot;  
  CTreeParameterArray  m_parameters;  // (2)
public:
  MyEventProcessor(unsigned short id, 
		   unsigned short slot, 
		   std::string    prefix);  // (3)           

  virtual Bool_t operator()(const Address_t pEvent,
			    CEvent&         rEvent,
			    CAnalyzer&      rAnalyzer,
			    CBufferDecoder& rDecoder);


};

#endif

                    

The changed sections are marked:

(1)
We must include the <TreeParameter.h> header in order to get the class definitions.
(2)
Instead of having an integer member that tells us the index of the first target parameter, we will have a CTreeParameterArray into which we will unpack the data from our packet.
(3)
In order to make the event processor as re-usable as our old one, we pass a string; 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.

3.4.2.2. Modifying MyEventProcessor.cpp to use TreeParameters.

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) : // (1)
  m_id(id),
  m_slot(slot)                                             // (2)
{
  string arrayName = prefix;
  arrayName       += '.';                   // (3)
  arrayName       += "times";

  m_parameters.Initialize(arrayName,
			  4096, 0.0, 4095.0, // (4)
			  string("channels"),
			  32, 0);
}

                    

(1)
We have modified the implementation's interface as well to reflect the change in the header.
(2)
We allow the 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.
(3)
This part of the constructor creates the tree parameter's array name by grafting the prefix onto the text times separated by a period.
(4)
This call initializes the tree parameter array. The three parameters immediately after the 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:

Example 3-24. Constructing the event processor for Tree parameters


void
CMySpecTclApp::CreateAnalysisPipeline(CAnalyzer& rAnalyzer)
{

    RegisterEventProcessor(*(new MyEventProcessor(0x8100, 10, "test")), "CaenRaw");
}
                    
In this example we have chosen the prefix test so the names of the parameters created will be e.g. test.times.0.

3.4.2.3. Using the Gui to create definitions files.

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.

Warning

The treeparameter GUI for SpecTcl 3.0 and earlier will behave in unpredictable ways if you have:

  • A tree parameter with a name that has no period.

  • No tree variables declared (this is why we left the sample code in MySpecTclApp.cpp in place.

The GUI for SpecTcl 3.1 does not suffer from these problems.

3.4.2.3.1. Creating an array of spectra

The Tree parameter GUI for SpecTcl 3.0 is shown in the figure below:

Figure 3-5. SpecTcl-3.0 Tree parameter GUI

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

  1. Check the array checkbutton in the buff colored strip of controls.

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

  3. 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:

Figure 3-6. Tree Gui for SpecTcl-3.0 ready to create a spectrum array

To finish creating the spectra click the Create/Replace button. This will generate the spectra test.times.00, test.times.01 ... test.times.31.

3.4.2.3.2. Creating the 2-d spectrum

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.

  1. Check the 2d radio button in the Spectrum type controls box in the upper left of the cyan part of the GUI

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

  3. Set the Y parameter to test.times.01

  4. In order to keep this spectrum small, adjust the bins on both the X and Y parameters to be 256.

  5. Click the Create/Replace button to create the spectrum.

3.4.2.3.3. Saving definitions

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.

Example 3-27. Using Tree Gui to save definitions to file

Saving spectrum definitions to file

  1. Click the Save button. This button is located in the upper right corner of the Cyan part of the gui.

  2. In the flie selection dialog that pops up, enter myspectra.

  3. Click the Save button in the dialog.

3.4.2.3.4. Loading 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

  1. Click the Load button.

  2. In the file chooser that pops up, select or type in myspectra.tcl

  3. Click the Open 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 cumulate 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.

3.4.2.4. Testing our work

To test our work:

  • Run SpecTcl and read in our configuration file.

  • Display the spectrum test.times.00 in Xamine.

  • Click the Attach online button to attach SpecTcl to the online system.

  • Run your packetized Readout program and start a run.

You should see counts in the spectrum you have displayed.

3.4.3. Using tree variables to implement a weighted sum

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:

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;              // (1)

  CTreeVariable     m_weight1;             // (2)
  CTreeVariable     m_weight2;
  
  CTreeParameter    m_output;             // (3)
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

                
(1)
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.
(2)
These two member variables will be tree variables that store the weights to use when computing the sum.
(3)
This member is a tree variable into which we will store the weighted sum computed by the event processor.

Now lets look at the implementation:

Example 3-31. First implementation of the weighted sum computer


WSUM1CPP;
                
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:

  1. Modify Makefile so that it compiles and links in wsum.cpp

  2. Modify MySpecTclApp.cpp to include the wsum.h headerr.

  3. 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");
    }
                            

Rebuild SpecTcl, run it and read in your saved configuration file. Create a new 1-d spectrum named sum for computed.sum. Save this configuration back to file. In order to keep the range of the computed parameter within the spectrum, we need to set values for the weights to something other than 1.0 (their initial value We can do this using the TreeParameter gui.

Click on the Variables tab of the tree parameter gui. See below for an image of the GUI after you do that.

Figure 3-7. Tree variable tab of the GUI.

The radio button allows you to select a slot on the GUI. The Variable lable is actually a button that brings up a pull down navigator of the variable name hierarchy. The Load buttons reload the current value and units of the variable (or array if the array checkbox is set) into the selected line, while the Set button sets the values/units of the variable or array to the the values inthe Value and Unit column.

Use the Variable 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:

Perform these tests, be sure to click the Set button after you change each variable.

3.4.3.1. Generalizing the weighted sum computer.

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.

In this section we will improve on our weigthed sum computer. We will solve the problems above by using the parameter and variable search facilities to locate parameters and variables by name.

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;
                                      // (1)
  std::string     m_weight1Name;
  std::string     m_weight2Name;

  CTreeParameter* m_param1;
  CTreeParameter* m_param2;
                                     // (2)
  CTreeVariable*  m_weight1;
  CTreeVariable*  m_weight2;
public:
  wsum(std::string param1,  std::string param2,
       std::string weight1, std::string weight2); // (3)

  virtual Bool_t OnAttach(CAnalyzer& rAnalyzer);  // (4)
  
  virtual Bool_t operator()(const Address_t pEvent,
			    CEvent&         rEvent,
			    CAnalyzer&      rAnalyzer,
			    CBufferDecoder& rDecoder);
  
  
};
#endif

                    
(1)
The first set of member data will hold the names of the parameters. We get the names at construction time, but locate and save the parameters and variables at attach time. This ensures that Enough of the mechanics of the tree paramter system is alive when we need it.
(2)
Provides the storage for pointesr to the four objectgs we will need.
(3)
Tnis construction requires a set of names rather than objects.
(4)
The 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)
{}
                    
All that is needed is to initialize the string members.

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;

}