Download the latest elder-pt release (e.g. elderpt-0.1.tar.gz).
tar -zxvf elderpt-0.1.tar.gz. Change into the unpacked directory cd elderpt-0.1.
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.
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.
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".
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.

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.

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

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.

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

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.

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.
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.
![]()
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.
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.
##########################################################
# 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)
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;
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
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
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.
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
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