Skip to content

Processing Categorized Data

This section explains how to create a new processor using categorized data (catdata) generated by art::TMappingProcessor. Such processors are referred to as Mapping Processors. The classification of data is defined in the mapper.conf and conf/map files. For details on creating map files, see Map Configuration.

In this section, we will:

  1. Create a processor to extract specific data defined in the map file and store it in art::TSimpleData.
  2. Explore the structure of catdata.

The overall process is similar to what was discussed in the previous section.

Processor Overview

flowchart LR
    A("**RIDF data files**") --> B("<u>**art::TRIDFEventStore**</u><br>input: RIDF files<br>output: segdata")
    B --> C("<u>**art::TMappingProcessor**</u><br>input: segdata<br>output: catdata")
    C --> D{"<u>**art::crib::TMapSelector**</u>"}

We will create the following processor:

  • Name: TMapSelector
  • Namespace: art::crib (for CRIB-specific code)
  • Input: catdata
  • Output: A branch with elements of art::TSimpleData stored in a TClonesArray

Map File Example

A sample map file looks like this:

10, 0,  12 1  6   3  0,  12 2  7  0  16
10, 1,  12 1  6   3  1,  12 2  7  0  17

In this format:

  • The first column specifies the catid.
  • The second column specifies the detid.
  • Subsequent groups of five numbers represent segid values, with typeid differentiating between these groups.

For example, specifying CatID: [10, 1, 1] extracts segid = [12, 2, 7, 0, 17].

Example Steering File

A steering file is used to define parameters like CatID and the output branch name. Below is a sample configuration:

Anchor:
  - &input ridf/@NAME@@NUM@.ridf
  - &output output/@NAME@/@NUM@/test@NAME@@NUM@.root

Processor:
  - name: timer
    type: art::TTimerProcessor

  - name: ridf
    type: art::TRIDFEventStore
    parameter:
      OutputTransparency: 1
      InputFiles:
        - *input

  - name: mapper
    type: art::TMappingProcessor
    parameter:
      OutputTransparency: 1

  - name: map_channel
    type: art::crib::TMapSelector
    parameter:
      OutputCollection: channel
      CatIDs: [10, 1, 1]

  - name: outputtree
    type: art::TOutputTreeProcessor
    parameter:
      FileName:
        - *output

Working with catdata

Creating TMapSelector

Begin by creating the header and source files for TMapSelector. Don’t forget to register these files in artcrib_linkdef.h and CMakeLists.txt. For details, refer to the General processor.

Header File

The header file defines the required components and variables for handling catdata:

#ifndef _CRIB_TMAPSELECTOR_H_
#define _CRIB_TMAPSELECTOR_H_

#include "TProcessor.h"

class TClonesArray;

namespace art {
class TCategorizedData;
} // namespace art

namespace art::crib {
class TMapSelector : public TProcessor {
  public:
    TMapSelector();
    ~TMapSelector();

    void Init(TEventCollection *col) override;
    void Process() override;

  private:
    TString fCategorizedDataName;
    TString fOutputColName;

    IntVec_t fCatID; //! [cid, id, type]

    TCategorizedData **fCategorizedData; //!
    TClonesArray *fOutData;             //!

    TMapSelector(const TMapSelector &) = delete;
    TMapSelector &operator=(const TMapSelector &) = delete;

    ClassDefOverride(TMapSelector, 1);
};
} // namespace art::crib

#endif // end of #ifndef _CRIB_TMAPSELECTOR_H_

Source File

Prepare the source file to receive catdata in a manner similar to how segdata is handled. The Process() method will be implemented later.

#include "TMapSelector.h"

#include <TCategorizedData.h>
#include <TRawDataObject.h>
#include <TSimpleData.h>

ClassImp(art::crib::TMapSelector);

namespace art::crib {
TMapSelector::TMapSelector() : fCategorizedData(nullptr), fOutData(nullptr) {
    RegisterInputCollection("CategorizedDataName", "name of the segmented data",
                            fCategorizedDataName, TString("catdata"));
    RegisterOutputCollection("OutputCollection", "name of the output branch",
                             fOutputColName, TString("channel"));

    IntVec_t init_i_vec;
    RegisterProcessorParameter("CatID", "Categorized ID, [cid, id, type]",
                               fCatID, init_i_vec);
}

void TMapSelector::Init(TEventCollection *col) {
    // Categorized data initialization
    void** cat_ref = col->GetObjectRef(fCategorizedDataName);
    if (!cat_ref) {
        SetStateError(Form("No input collection '%s'", fCategorizedDataName.Data()));
        return;
    }

    auto *cat_obj = static_cast<TObject *>(*cat_ref);
    if (!cat_obj->InheritsFrom("art::TCategorizedData")) {
        SetStateError(Form("Invalid input collection '%s': not TCategorizedData",
                           fCategorizedDataName.Data()));
        return;
    }
    fCategorizedData = reinterpret_cast<TCategorizedData **>(cat_ref);

    // CatID validation
    if (fCatID.size() != 3) {
        SetStateError("CatID must contain exactly 3 elements: [cid, id, type]");
        return;
    }

    fOutData = new TClonesArray("art::TSimpleData");
    fOutData->SetName(fOutputColName);
    col->Add(fOutputColName, fOutData, fOutputIsTransparent);
    Info("Init", "%s -> %s, CatID = %d",
         fCategorizedDataName.Data(), fOutputColName.Data(), fCatID[0]);
}
} // namespace art::crib

Structure of catdata

catdata is a hierarchical object composed of nested TObjArray instances. For further details, refer to the TCategorizedData.cc implementation.

The structure can be visualized as follows:

catdata (Array of categories)
├── [Category 0] (TObjArray)
│    ├── [Detector 0] (TObjArray)
│    │    ├── [Type 0] (TObjArray)
│    │    │    ├── TRawDataObject
│    │    │    ├── TRawDataObject
│    │    │    └── ...
│    │    ├── [Type 1] (TObjArray)
│    │    │    ├── TRawDataObject
│    │    │    └── ...
│    │    └── ...
│    └── [Detector 1] (TObjArray)
│         ├── [Type 0] (TObjArray)
│         └── ...
├── [Category 1] (TObjArray)
│    └── ...
└── ...

Key relationships:

  • Category corresponds to the first column (catid) in the map file.
  • Detector ID corresponds to the second column (detid).
  • Type identifies the specific group (segid) referred to.

Extracting a Category (catid)

To retrieve a specific category, use the FindCategory(catid) method:

TObjArray* det_array = categorizedData->FindCategory(catid);

This returns an array corresponding to a row in the map file.

Extracting a Detector ID (detid, id)

To retrieve a specific detid from the category array, access it using:

TObjArray* type_array = (TObjArray*) det_array->At(index);
**Note**: The `index` does not directly correspond to the `detid`. The actual `detid` value is stored within the object and must be accessed programmatically.

Extracting Data (from type)

To extract data (art::TRawDataObject) from the type_array, use the following:

TObjArray* data_array = (TObjArray*) type_array->At(typeIndex);
TRawDataObject* data = (TRawDataObject*) data_array->At(dataIndex);

The type_array is created using the AddAtAndExpand method of TObjArray, meaning its size corresponds to the map file and can be accessed by index.

Each element in type_array is also a TObjArray, designed to handle multi-hit TDC data. Use the At method to access individual elements and cast them to TRawDataObject.

Displaying Data

Here is an example of how to extract and display data for a specific catid, such as catid = 7:

void TMapSelector::Process() {
    if (!fCategorizedData) {
        Warning("Process", "No CategorizedData object");
        return;
    }

    auto *cat_array = fCategorizedData->FindCategory(7); // Specify catid
    if (!cat_array)
        return;

    const int nDet = cat_array->GetEntriesFast();
    for (int iDet = 0; iDet < nDet; ++iDet) {
        auto *det_array = static_cast<TObjArray *>(cat_array->At(iDet));
        const int nType = det_array->GetEntriesFast();
        for (int iType = 0; iType < nType; ++iType) {
            auto *data_array = static_cast<TObjArray *>(det_array->At(iType));
            const int nData = data_array->GetEntriesFast();
            for (int iData = 0; iData < nData; ++iData) {
                auto *data = dynamic_cast<TRawDataObject *>(data_array->At(iData));
                int id = data->GetSegID();
                // id is generated by `id = (dev << 20) + (fp << 14) + (mod << 8)`
                int dev = (id >> 20) & 0xFFF;
                int fp = (id >> 14) & 0x3F;
                int mod = (id >> 8) & 0x3F;

                std::cout << "dev=" << dev << " fp=" << fp << " mod=" << mod << " geo=" << data->GetGeo() << " ch=" << data->GetCh()
                          << " : catid=" << data->GetCatID() << " detid=" << data->GetDetID() << " typeid=" << data->GetType()
                          << " : detIndex=" << iDet << " typeIndex=" << iType << " dataIndex=" << iData << std::endl;
            }
        }
    }
}

Example Output:

dev=12 fp=0 mod=7 geo=1 ch=75 : catid=7 detid=25 typeid=0 : detIndex=0 typeIndex=0 dataIndex=0
dev=12 fp=0 mod=7 geo=1 ch=75 : catid=7 detid=25 typeid=0 : detIndex=0 typeIndex=0 dataIndex=1
dev=12 fp=0 mod=7 geo=1 ch=84 : catid=7 detid=54 typeid=0 : detIndex=1 typeIndex=0 dataIndex=0
dev=12 fp=0 mod=7 geo=1 ch=84 : catid=7 detid=54 typeid=0 : detIndex=1 typeIndex=0 dataIndex=1
dev=12 fp=0 mod=7 geo=1 ch=84 : catid=7 detid=54 typeid=0 : detIndex=1 typeIndex=0 dataIndex=2
dev=12 fp=0 mod=7 geo=1 ch=84 : catid=7 detid=54 typeid=0 : detIndex=1 typeIndex=0 dataIndex=3
...

Implementing TMapSelector

The Process() method extracts data for a specific channel, matching detid (fCatID[1]) and storing values in art::TSimpleData. Error handling is omitted for brevity.

void TMapSelector::Process() {
    fOutData->Clear("C");

    auto *cat_array = fCategorizedData->FindCategory(fCatID[0]);
    const int nDet = cat_array->GetEntriesFast();
    int counter = 0;
    for (int iDet = 0; iDet < nDet; ++iDet) {
        auto *det_array = static_cast<TObjArray *>(cat_array->At(iDet));
        auto *data_array = static_cast<TObjArray *>(det_array->At(fCatID[2]));
        const int nData = data_array->GetEntriesFast();
        for (int iData = 0; iData < nData; ++iData) {
            auto *data = dynamic_cast<TRawDataObject *>(data_array->At(iData));
            if (data && data->GetDetID() == fCatID[1]) {
                auto *outData = static_cast<art::TSimpleData *>(fOutData->ConstructedAt(counter));
                counter++;
                outData->SetValue(data->GetValue());
            }
        }
    }
}

Verification

To verify consistency with the previous section (TChannelSelector), compare the extracted catid and segid using the following commands:

artlogin <usename>
a
artemis [] add steering/hoge.yaml NAME=xxxx NUM=xxxx
artemis [] res
artemis [] sus
artemis [] fcd 0
artemis [] tree->Scan("channel.fValue:mapchannel.fValue")

Example Output:

***********************************************
*    Row   * Instance * channel.f * mapchanne *
***********************************************
*        0 *        0 *     20843 *     20843 *
*        0 *        1 *     21394 *     21394 *
*        1 *        0 *           *           *
*        2 *        0 *           *           *
*        3 *        0 *     19049 *     19049 *
*        3 *        1 *     19665 *     19665 *
*        4 *        0 *           *           *
*        5 *        0 *           *           *
*        6 *        0 *     24904 *     24904 *
*        6 *        1 *     25490 *     25490 *
*        7 *        0 *           *           *

For full implementation details, see: