Declaration and production of tables

Goal

Learn how to declare and produce new tables.

source: newCollections.cxx
Executable: o2-analysistutorial-new-collections

Before we come to discussing the tutorial code a few general words about creating tables.

Declaration of tables

The first step in creating a table in O2 is to declare it, hence specify its name, some additional parameters, and it's basic components, the columns.

The O2 framework provides a few methods to declare tables in analysis tasks. See the list below. Click on the titles to display information about the arguments and the resulting tables.

All methods have a Name and Description argument. The argument Name is used to define the type of the table which is o2::aod::Name. The Description argument is a string which is used within the framework to identify the table. It has a maximum length of 16 characters. Origin has to be set to "AOD".

DECLARE_SOA_TABLE (Name, Origin, Description, ...);
Declares a table of type `Name` with the columns specified in the argument list. The columns are specified as a comma separated list of column types. Only columns, dynamic columns, and index columns are accepted.
Method Type
asArrowTable( ) std::shared_ptr<arrow::Table> Type erased arrow table. Allows to apply the full functionality of Apache arrow tables.
getId<T> ( )
size ( ) or tableSize ( ) int64_t The number of rows the table contains.
begin ( ) First row.
iteratorAt ( uint64_t i) ith row.
end ( ) Last row.
select (framework::expressions::Filter&& f) Rows of table for which filter f returns true.
scliceBy<T>(colId, int value) Rows of table for which (colId == value) is true. colId is an index column.
DECLARE_SOA_EXTENDED_TABLE_USER (Name, Table, char* Description, ...);
Declares a table of type `Name` which contains the columns of table `Table` and in addition the expression columns specified in the argument list. Only expression columns are accepted.
DECLARE_SOA_INDEX_TABLE_USER (Name, Key, char* Description, ...);
Declares an index table of type `Name` with key table `Key` and a descriptive string `Description`. The remaining arguments are index columns to tables and are specified by `Table`Id. Index tables are used to access matching rows from non-joinable, but compatible tables.

Declaration of columns

Tables are basically collections of columns. The O2 framework provides the methods to declare columns, which are listed below. Click on the titles to display information about the arguments and the resulting columns.

Name and Getter are the common arguments of all methods. Name is used to define the type of the column which is namespace::Name where namespace is the namespace the column is declared in. Getter is the method which allows to access a column (tab.pt() e.g. gives access to an element of the column which was declared with a Getter value of pt).

DECLARE_SOA_COLUMN_FULL (Name, Getter, Type, char* Label);
Declares a column of type `Name`. The elements are of type `Type`. The column is given the label "f`Label`" which is used within the framework to identify the column.
Method Type
`Getter`( ) `Type` Column element.
DECLARE_SOA_COLUMN (Name, Getter, Type);
Same as DECLARE_SOA_COLUMN_FULL but here the label is by default set to "f`Name`".
DECLARE_SOA_EXPRESSION_COLUMN_FULL (Name, Getter, Type, char* Label, Expression);
Same as DECLARE_SOA_COLUMN_FULL but here the column element values are computed according to the expression `Expression`. Expression columns can be used to extend an existing table (see tutorial Extending Tables).
Method Type
`Getter`( ) `Type` Column element.
Projector( ) o2::framework::expressions::Projector `Expression`.
DECLARE_SOA_EXPRESSION_COLUMN (Name, Getter, Type, Expression);
Same as DECLARE_SOA_EXPRESSION_COLUMN_FULL but here the label is by default set to "f`Name`".
DECLARE_SOA_INDEX_COLUMN_FULL (Name, Getter, Type, Table, char* Suffix);
Declares the index column `Name`Id to the existing table `Table`s (binding table). The column elements are of type `Type`. `Suffix` can be used to distinguish several index columns to the same table. The column label which is used within the framework to identify the column is set to fIndex`Table``Suffix`. If `Suffix` is not empty it must start with an underscore! `Table` must have a o2::soa::Index column!
Method Type
`Getter`Id( ) `Type` Column element.
has_`Getter`( ) bool True if the binding table is not a NULL pointer.
`Getter`_as <T>( ) auto Respective row of (T*)binding table. T must include `Tables`s, hence can be e.g. a join including `Tables`s.
`Getter`( ) auto Respective row of table `Tables`s.
setCurrent <T>(T* t) bool Replace `Table`s by t as binding table.
getCurrent( ) `Table`s* Binding table.
DECLARE_SOA_INDEX_COLUMN_FULL (Name, Getter);
Same as DECLARE_SOA_INDEX_COLUMN_FULL but here the element type is by default set to int_32, the referencing table to `Name`s, and the label which is used within the framework to identify the column is accordingly set to fIndex`Name`.
DECLARE_SOA_DYNAMIC_COLUMN (Name, Getter, ...);
Declares a column of type `Name`. The column elements are dynamically computed with the lambda provided as third argument to the declaration. This also determines the type of the elements. Dynamic columns can be attached to existing tables (see tutorial Extending Tables).
Method Type
`Name`Callback functor The column lambda.
`Getter`<T...>(T... t) according to functor Dynamic column element computed with input columns t.


This tutorial demonstrates the creation and filling of normal tables. The usage of index tables is explained in tutorial Index Tables.

ProduceEtaPhi

In order to avoid naming conflicts between different tasks it is advisable to declare new columns in subspaces of the namespace o2::aod and the new tables in namespace o2::aod.

// declare columns in a sub-namespace of o2::aod
// and tables in namespace o2::aod
namespace o2::aod
{
namespace etaphi
{
DECLARE_SOA_COLUMN(Eta, eta, float);
DECLARE_SOA_COLUMN(Phi, phi, float);
} // namespace etaphi
DECLARE_SOA_TABLE(EtaPhi, "AOD", "ETAPHI",
                  etaphi::Eta, etaphi::Phi);
} // namespace o2::aod

Now that the table is declared we can use it to create a corresponding table object. This happens with the Produces class. Produces is a templated class and takes the type of the table to create as template argument. The table type in this case is aod::EtaPhi and the actual table object is etaphi.

The filling of the table etaphi is done with the method (… ) which takes as many arguments as columns are available.

struct ProduceEtaPhi {
  // declare production of table etaphi
  Produces<aod::EtaPhi> etaphi;

  void process(aod::Tracks const& tracks)
  {
    for (auto& track : tracks) {
      float phi = asin(track.snp()) + track.alpha() + static_cast<float>(M_PI);
      float eta = log(tan(0.25f * static_cast<float>(M_PI) - 0.5f * atan(track.tgl())));

      // update the table etaphi
      etaphi(phi, eta);
    }
  }
};

ConsumeEtaPhi and LoopEtaPhi

Within all tasks of a workflow the such created and filled table is available and hence can be use for further calculations. This is demonstrated with the tasks ConsumeEtaPhi and LoopEtaPhi of this tutorial.