Menu

Home

Michael Reese
Attachments
Go4_correlation.png (136480 bytes)
Go4_histogram_tree.png (93885 bytes)
Go4_histogram_view.png (113381 bytes)
Go4_launch_analysis.png (71826 bytes)
Go4_select_plugin.png (26624 bytes)
Go4_submit_start.png (31490 bytes)
logo.svg (13372 bytes)
logo2.svg (9444 bytes)
simple_graph.png (34838 bytes)
tracking_detector.png (17217 bytes)

elder-pt documentation

installation

Download the latest elder-pt release (e.g. elderpt-0.1.tar.gz).


Unpack the file tar -zxvf elderpt-0.1.tar.gz. Change into the unpacked directory cd elderpt-0.1.

configure

If you want to install into the system path you need root priviliges. If this is the case type ./configure. If this is not the case you can intall into your home directory (e.g. $HOME/.local). In this case type ./configure --prefix=$HOME/.local.

compile

Then type make -j 8. If your c++ compiler is too old (you'll get compilation errors in that case) you may have to type make CXXFLAGS=-std=c++11 -j 8.

install

In case you want a system wide installation, type sudo make install. In case you're installing into your home directory type make install. The installation of elder-pt is now complete.
Note that in case you've installed elder-pt into your home directory your may need to set the LD_LIBRARY_PATH environment variable before you start any program that uses elder-pt, e.g. in order to start go4 (see below) type LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$HOME/.local/bin" go4.
Alternatively, you can define an alias in your .profile
alias go4='LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$HOME/.local/lib" go4'
and
alias go4analysis='LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$HOME/.local/lib" go4analysis'.
If you want to compile any program that uses elder-pt, in particular elder-pt plugins, and elder-pt was installed into your home directory, you need to set the PKG_CONFIG_PATH environment variable. It is a good idea to set it in your .profile
export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$HOME/.local/lib/pkgconfig".

introduction to elder-pt

Elder-pt is a data reduction framework, that means it does not come with any executable program, but it can be used to build programs for data reduction. Examples of such programs are contained in the examples subdirectory of the elder-pt root directory. A typical font-end program that can be adapted to use elder-pt is Go4. In the following I assume that both, elder-pt and Go4 are installed on the system (to set the Go4 environment, the go4login script has to be sourced. That script is part of the Go4 installation).
The elder-pt distribution contains examples/Go4elder example. Enter this directory and type make then sudo make install if elder-pt is a system wide installation, or make install if elder-pt is installed in your home directory.
This will install a shared library libGo4elder.so in the directory where the elder-pt shared libraries are installed.
After that, you can enter any directory that provides a file called analysis.config containing the toplevel description of the data flow graph. A very simple example is provided in examples/random. Enter that directory and launch Go4. Click on the green "Launch analysis" button in the top toolbar.
Go4_screenshot
A popup window will appear and you should enter absolut path to the examples/random directory in the Dir line, and libGo4elder.so in the Lib line.
Go4_screenshot
Then click on the green hook. A data selection popup window will appear, where you have to select the random source as shown in the following picture (this is the only event source in Go4 that doesn't need a data input file).
Go4_screenshot
The go4 autosave function doesn't work well together with the elder-pt framework. It is recommended disable it. First uncheck the Enabled checkbox in the section Auto Save File, then click on Submit and then click on the floppy disk button in the section Analysis Configuration File.
Click on Submit+Start button to start the data processing. Double-click on the Analysis entry in the tree view on the left hand side of the GUI to open the histogram browser. Double-click any of the histograms in the browser to see it in the main window of the GUI. Both histograms show a Gaussian distribution of events.
Go4_screenshot
Congratulations! You have a working system, consisting of a Go4 frontent for data visualization with an elder-pt backend that can be completely customized by a script. The toplevel script for this implemetation of the Go4elder frontent is called analysis.config. All histograms that you see in the Go4 GUI are defined therein.

using std

processor random_x std.rand_gaussian
    parameter mean  = 5
    parameter sigma = 1
    histogram value
end

processor random_y std.rand_gaussian
    parameter mean  = 10
    parameter sigma = 1
    histogram value
end

syntax

Processors are the building blocks of the data analysis / data reduction graph. They can be defined by the processor keyword, followed by the processor name and its type, and ends with the end keyword.
Processors form the nodes of the data flow graph. Data is copied along the edges. In the previous example, we only had two processor nodes. We can look at the correalation of the two random values by using a third processor:

processor correlation std.pair
    first  <- random_x.value
    second <- random_y.value
    histogram first:second
end

After adding these lines to the file analysis.config, you only need to click on the Submit+Start button in the Go4 toolbar to restart the analysis: all histograms are removed, the file analysis.config is read again and the data processing is started. A new histogram should appear which looks like this:
Go4_screenshot
You have created a data flow graph with three nodes. Two of them serve as data sources (Gaussian random numbers) and the third one is used to create a correlation histogram of the two independent sources. This graph is drawn in the following picture.
schematic_graph_representation
Graph nodes (processors) and the connecting edges that specify the data flow are draw in red. The internals of each node are drawn in black. The name can be chosen when placing a processor in a graph. The type specifies the internals (inputs / outputs) of the node. The processor types used in this example are all from the stdandard library of elder-pt. It is easy to write your own library with custom processor types.

adding plugin libraries

Consider the following situation: In a beam line there are two position and time sensitive detectors in a certain distance. The detector arrangement is used to track charged particles that penetrates the two detectors.
detector_setup
I'll show how to write an elder-pt plugin with a tracking processor to calculate the particle velocity and direction based on the information from two such planar detectors, and how to use this processor in an elder-pt analysis.

the structure of the plugin

Create a directory with name tracking and the following strucure:

tracking/
|-- makefile
|-- module.cpp
|-- process
|   |-- TwoPlaneTracker.cpp
|   |-- TwoPlaneTracker.hpp

The makefile is the same for all elder-pt plugins and no modificationa are needed.
The module.cpp is a list of all components (processors, unpackers, sources) of the elder-pt plugin. Unitl now we have only seen processors (like std.rand_gaussian or std.pair), and we will ignore unpackers and sources for now.
The process subdirectory contains header and implementation of each processor. In this case we'll write a processor with name TwoPlaneTracker which takes as input two sets of (x,y,t) coordinates, i.e. the x and y position and the time in each detection plane.
Later, additional processors can be added by creating a new pair of .hpp and .cpp files and adding the new processor into module.cpp.

makefile

##########################################################
# No need to modify this file after adding processors 
# or unpackers to the module.cpp
##########################################################
LIBRARY = $(shell echo libelderpt`pwd | rev | cut -d'/' -f1 | rev`.so)
OBJECTS = $(shell find -iname "*.cpp" | sed 's/.\/module.cpp//g' | sed 's/module.cpp//g'| sed 's/.cpp/.o/g')
HEADERS = $(shell find -iname "*.hpp")

ELDERPT_CFLAGS = `pkg-config elderpt-0.1 --cflags` 
ELDERPT_LIBS   = `pkg-config elderpt-0.1 --libs`  -lasound 

all: $(LIBRARY) 

%.o : %.cpp %.hpp
    g++ -O3 $(ELDERPT_CFLAGS) -c -g -fPIC -shared $< -o $@
%.o : %.cpp 
    g++ -O3 $(ELDERPT_CFLAGS) -c -g -fPIC -shared $< -o $@

$(LIBRARY): $(OBJECTS) $(HEADERS) module.cpp
    g++ -O3 $(ELDERPT_CFLAGS) $(ELDERPT_LIBS) -fPIC -shared -g -o $(LIBRARY) $(OBJECTS) module.cpp

.PHONY: install uninstall clean

install: all
    pkg-config elderpt-0.1 --libs | sed 's/ /\n/g' | head -n 1 | sed 's/-L//g' > .install.dir
    cp $(LIBRARY) `cat .install.dir`
    @echo installed $(LIBRARY) into `cat .install.dir`

uninstall:
    @rm `cat .install.dir`/$(LIBRARY)
    @echo removed $(LIBRARY) from `cat .install.dir`

clean:
    rm -f $(OBJECTS) $(LIBRARY)

module.cpp

This file lists all processors between the ELDER_MODULE_PROCESSOR_BEGIN; and ELDER_MODULE_PROCESSOR_END; macros.
The processor list consist of calls of the ELDER_MODULE_PROCESSOR_ADD(); macro.
We ignore the unpackers and sources for now.

#include <elderpt.hpp>

#include "process/TwoPlaneTracker.hpp"

ELDER_MODULE_PROCESSOR_BEGIN;
ELDER_MODULE_PROCESSOR_ADD(elderpt_plugin::tracking::TwoPlaneTracker);
ELDER_MODULE_PROCESSOR_END;

ELDER_MODULE_UNPACKER_BEGIN;
ELDER_MODULE_UNPACKER_END;

ELDER_MODULE_SOURCE_BEGIN;
ELDER_MODULE_SOURCE_END;

process/TwoPlaneTracker.hpp

This is the header file for the TwoPlaneTracker. It contains one class declaration that publicly derives from elderpt::process::Processor. It is good practice to place the class in an appropriately named namespace.
The class defines its inputs, outputs and parameters as enums. It has to provide at least two member functions void initialize() and void process(). It is good practice to document all inputs, outputs and parameters here.

#ifndef TWO_PLANE_TRACKER_HPP_
#define TWO_PLANE_TRACKER_HPP_

#include <elderpt/process/Processor.hpp>

#include <iostream>
#include <deque>

namespace elderpt_plugin // this namespace must be there for all elderpt plugins
{

namespace tracking  // this is the name of the elder plugin
{


class TwoPlaneTracker : public elderpt::process::Processor
{
public:
    void initialize();
    void process();

    enum Parameter
    {
        plane_distance_z, // distance of the two detection planes [in units of mm]
    };                    // z-direction points downstream along the beam line

    enum Input
    {
        plane1_x, // x position on the first detection plane [in units of mm]
        plane1_y, // y position on the first detection plane [in units of mm]
        plane1_t, // time at the first detection plane       [in units of ns]
        plane2_x, // x position on the second detection plane [in units of mm]
        plane2_y, // y position on the second detection plane [in units of mm]
        plane2_t, // time at the second detection plane       [in units of ns]
    };

    enum Output
    {
        v_x, // velocity vector x-coordinate [in units of speed of light]
        v_y, // velocity vector y-coordinate [in units of speed of light]
        v_z, // velocity vector z-coordinate [in units of speed of light]
        v_abs, // absolute value of velocity [in units of speed of light]
    };
private:
};
} // namespace tracker
} // namespace elderpt_plugin
#endif

process/TwoPlaneTracker.cpp

This file contians the implementation of the initialize() and process() member function.
Inside initialize(), the macro NAME_PARAMETER() has to be called for each of the elements in the Parameter enum defined in TwoPlaneTracker.hpp.
After that, default values for parameters can be defined optionally.
After that the function read_paramets() has to be called.
Then the macro NAME_INPUT_CHANNEL() has to be called for each of the elements in the Input enum defined in TwoPlaneTracker.hpp.
Then the macro NAME_OUPUT_CHANNEL() has to be called for each of the elements in the Output enum defined in TwoPlaneTracker.hpp.

The member function process() contains the calculation of the outputs based on the inputs.
For any computation that depends on a given input (e.g. plane1_x), one has to check if that input is valid. A value can be invalid if there is no data source attached to the input of that processor, or if the attached processor gave an invalid output.
The validity of any input (e.g. plane1_x can be checked by calling input_valid(plane1_x). Remember that plane1_x refers tho the Input enum element plane1_x defined in TwoPlaneTracker.hpp.
If all inputs that are neede for the computation are validated, the actual value can be obtained by calling input_value(plane1_x). The result is a double and can be directly used or assigned to a double variable for computation.
Finally, the ouputs (e.g. v_abs) of the Processor are filled by calling the function set_output(v_abs, sqrt(vx*vx + vy*vy + vz*vz)/c) where the first parameter refers to an element of the Output enum defined in TwoPlaneTracker.hpp.
The process() member function will be called by the elder-pt framework for each event that has to be processed. Where the input comes from and where the output goes to is defined inside the analysis.config file that we have seen before.

#include "TwoPlaneTracker.hpp"
#include <iostream>
#include <vector>
#include <cstdint>
#include <cmath>

namespace elderpt_plugin 
{
namespace tracking
{
void TwoPlaneTracker::initialize()
{
    // define the parameter 
    NAME_PARAMETER(plane_distance_z);
    // give a default value for the parameter
    parameter(plane_distance_z) = 1000; // default distance of 1000 mm = 1 meter
    // read parameter from file (overriding the default value)
    read_parameters();
    // crate the input ports
    NAME_INPUT_CHANNEL(plane1_x);
    NAME_INPUT_CHANNEL(plane1_y);
    NAME_INPUT_CHANNEL(plane1_t);
    NAME_INPUT_CHANNEL(plane2_x);
    NAME_INPUT_CHANNEL(plane2_y);
    NAME_INPUT_CHANNEL(plane2_t);
    // crate the output ports
    NAME_OUTPUT_CHANNEL(v_x);
    NAME_OUTPUT_CHANNEL(v_y);
    NAME_OUTPUT_CHANNEL(v_z);
    NAME_OUTPUT_CHANNEL(v_abs);
}

void TwoPlaneTracker::process()
{
    static const double c = 299.79; // speed of light in mm/ns
    // check if all inputs are valid (the "input_valid" function is provided by the Processor base class)
    if (input_valid(plane1_x) && input_valid(plane1_y) && input_valid(plane1_t) && 
        input_valid(plane2_x) && input_valid(plane2_y) && input_valid(plane2_t)) {
        // set the output values
        double dt = input_value(plane2_t) - input_value(plane1_t);
        double dx = input_value(plane2_x) - input_value(plane1_x);
        double dy = input_value(plane2_y) - input_value(plane1_y);
        double dz = parameter(plane_distance_z);
        // calculate velocity in units of mm/ns
        double vx = dx/dt; 
        double vy = dy/dt;
        double vz = dz/dt;
        // output velocity in units of c;
        set_output(v_x, vx/c);
        set_output(v_y, vy/c);
        set_output(v_z, vz/c);
        set_output(v_abs, sqrt(vx*vx + vy*vy + vz*vz)/c);
    }
}
} // namespace tracker
} // namespace elderpt_plugin

compiling the plugin

The plugin can be compiled by typing make, and installed by typing make install or, if the elder-pt installation is system wide sudo make install.

using the plugin

In order to test the plugin, we can write a file analysis.config with the following content.

using std    # use the standard processors
using tracking # use the processors insid the new tracking plugin

# first detection plane: Gaussian beam profile with 10 mm sigma arriving at t=0ns with dt=1ns
processor plane1_x std.rand_gaussian
    parameter mean  = 0
    parameter sigma = 10
end
processor plane1_y std.rand_gaussian
    parameter mean  = 0
    parameter sigma = 10
end
processor plane1_t std.rand_gaussian
    parameter mean  = 0
    parameter sigma = 1
end

# second detection plane: Gaussian beam profile with 10 mm sigma arriving at t=10ns with dt=1ns
processor plane2_x std.rand_gaussian
    parameter mean  = 0
    parameter sigma = 10
end
processor plane2_y std.rand_gaussian
    parameter mean  = 0
    parameter sigma = 10
end
processor plane2_t std.rand_gaussian
    parameter mean  = 10
    parameter sigma = 1
end

# create a TwoPlaneTracker and fill it with the random values from above
processor tracker tracking.TwoPlaneTracker
    plane1_x <- plane1_x.value
    plane1_y <- plane1_y.value
    plane1_t <- plane1_t.value
    plane2_x <- plane2_x.value
    plane2_y <- plane2_y.value
    plane2_t <- plane2_t.value
    # create some histograms 
    histogram v_abs 10000
    histogram plane1_x:plane2_x
    histogram plane1_t:v_abs
end

For loops in config files

There is a log of redundancy in the analysis.config file above. In order to reduce the size we can use a for loop. The following file does the exact same thing but is shorter and easyier to read:

using std
using tracking

# create four processors using two nested for loops
for $PLANE in 1 2 
    for $COORD in x y
        processor plane$PLANE_$COORD std.rand_gaussian
            parameter mean  = 0
            parameter sigma = 10
        end
    end 
end 
processor plane1_t std.rand_gaussian
    parameter mean  = 0
    parameter sigma = 1
end
processor plane2_t std.rand_gaussian
    parameter mean  = 10
    parameter sigma = 1
end

processor tracker tracking.TwoPlaneTracker
    plane1_x <- plane1_x.value
    plane1_y <- plane1_y.value
    plane1_t <- plane1_t.value
    plane2_x <- plane2_x.value
    plane2_y <- plane2_y.value
    plane2_t <- plane2_t.value
    histogram v_abs 10000
    histogram plane1_x:plane2_x
    histogram plane1_t:v_abs
end

Project Members: