(interpreted-dictionary)= # Interpreted ROOT dictionaries I find that the ROOT web site documentation for [creating a dictionary](https://root.cern/manual/io_custom_classes/) is a bit opaque. That's why I had to write my little test suite to understand how it worked. It's also why I'm sharing it with you. Part of the reason why I have difficulty with that page is that the method it discusses first, [ACLiC](https://root.cern/manual/root_macros_and_shared_libraries/), assumes that you're working with ROOT macros interactively. As you know from {ref}`Exercise 11 `, I prefer to use ROOT by compiling stand-alone programs. Also, ACLiC does not fit well with using [Python/pyroot](https://root.cern/manual/python/). So let's consider two other ways to create a ROOT dictionary: - Using an interpreter, which means that the dictionary is created at the time the program *executes*. That's discussed on this web page. - Compiled, which means that the dictionary is compiled into a binary load library, and is linked when the C++ program is *compiled*. That discussed on the next web page. When it comes to reading ROOT files using a dictionary, either method works with Python. I'll show examples later. ## The example custom class To start, you'll want to copy my dictionary-testing directory to your own area:[^download] [^download]: If you're not at Nevis, you can download the files from [here](https://www.nevis.columbia.edu/~seligman/root-class/files/root-dict/). :::{code-block} bash :name: copying-dictionary-files cp -arv ~seligman/root-class/root-dict $PWD cd root-dict # Suggestion: Delete the existing dictionary files # to see the dictionary-generation process in action. rm -f Auto* *.pcm *.so ::: Start with the file [STL_TrackList.h](https://www.nevis.columbia.edu/~seligman/root-class/files/root-dict/STL_TrackList.h). This shows a simplistic example of how data might be structured for particle tracks in a detector: a `struct` that describes a single track with multiple steps/scatters, and a class that contains a list of multiple tracks and provides access to that list. ## Creating the dictionary The file [STLntupleCreate.cxx](https://www.nevis.columbia.edu/~seligman/root-class/files/root-dict/STLntupleCreate.cxx) creates a TTree with several branches.[^standalone] The first couple of branches, **`run`** and **`event`**, are of type `int` and don't require any special handling beyond what is shown in {numref}`Listing %s `. The next two branches, a `vector` and a `vector`, also don't require anything special. [^standalone]: If you look at the comments and the overall organization of the `STL*.cxx` files in these examples, you may correctly deduce that they also illustrate the solution to {ref}`Exercise 11 `, the stand-alone C++ program. If you gave up on that exercise and decided to skim the appendices instead, congratulations! You have the solution! The remaining three branches all involve more complex structures that need a dictionary for ROOT I/O. For the description of `vMap_t` and `map2D_t`, see the file [STLntupleTypes.h](https://www.nevis.columbia.edu/~seligman/root-class/files/root-dict/STLntupleTypes.h).[^ungood] [^ungood]: I'll be the first to admit that these type names aren't very good. In real code, I'd probably use names that defined the kind of data represented by the collection; e.g., `WireGrid_type`. Remember, all of this represents a quick study on my part. The actual dictionary generation is in [STLntuple.icc](https://www.nevis.columbia.edu/~seligman/root-class/files/root-dict/STLntuple.icc). I put the dictionary-generation code in a separate file as a simplistic way to assure that any C++ program I wrote for this test would use the same commands. The "magic" happens with these lines: :::{code-block} c++ :name: dynamic-dict-generation :caption: The key C++ lines for generating a dictionary for `STL_TrackList.h` gInterpreter->GenerateDictionary("map", "map"); gInterpreter->GenerateDictionary("map, double>", "map;tuple"); gInterpreter->GenerateDictionary("TrackList", "STL_TrackList.h;map;vector"); ::: The ROOT global variable `gInterpreter` points to an instance of a [global command-line interpreter](https://root.cern.ch/doc/master/classTInterpreter.html). You can think of this as the program that would interpret ROOT commands line-by-line as you typed them into an interactive ROOT session (even though none of these programs are interactive). As the comments in the file indicate, we're actually generating three dictionaries, one for each branch in the TTree with a special C++ object. What does `TInterpreter::GenerateDictionary` actually do? It uses ROOT's internal C++ interpreter, [cling](https://root.cern/manual/cling/), to interpret the C++ code in the first argument, using the header files given in the second argument. It breaks those structures down into the primitive [leaves](https://root.cern.ch/doc/master/classTLeaf.html) that ROOT already knows how to write. This operation is using an [interpreter](https://www.geeksforgeeks.org/compiler-vs-interpreter-2/) instead of a compiler, so it takes some time. Fortunately, ROOT's dictionary generator is pretty smart: If a dictionary for a given object already exists in the current directory and has not changed, it won't go through the process again. You can test this if you like. Use the compilation statement at the top of `STLntupleCreate.cxx`: g++ `root-config --cflags --libs` STLntupleCreate.cxx -o STLntupleCreate Execute the program with: ./STLntupleCreate There will be a delay as ROOT creates the dictionaries for the first time.[^nodelay] If you just hit the up-arrow to execute the command again, or compile-and-execute the example in the next section, the program will execute almost immediately. If you update any of the header files in the `GenerateDictionary` method; e.g.,: touch STL_TrackList.h then if you re-run the programs there'll be another long pause as the dictionary is generated again. [^nodelay]: If there is no delay, or you get some kind of load library error, you probably copied over the AutoDict files from my directory. You may want to delete them so you can see for yourself how the process works: rm -f AutoDict* If you type `ls`, you'll see that new files with names of the form `AutoDict_*.cxx` have been created in your directory. As you've probably guessed, these contain the C++ source code for the dictionaries. `TInterpreter::GenerateDictionary` created those files, then used `TCling` to interpret them and create the libraries `AutoDict_*.so` and auxiliary files `AutoDict_*.pcm` and `AutoDict_*.d`. Those libaries are then loaded when ROOT does any I/O of these custom data collections. ## Reading with the dictionary Reading a `TTree` with a dictionary is roughly the same process as creating the `TTree`: Load the dictionary, then use the standard ROOT methods for input. Let's start with a quick skeleton of reading a variable from an n-tuple. :::{code-block} c++ :name: reading-ntuple :caption: An example of how to read a variable from a TTree in C++. Compare this with {numref}`Listing %s `. // Define the input file. auto input = new TFile("experiment.root"); // Define the which TTree to read. TTreeReader myReader("tree1", input); // Define which variable(s) we'll read. This behaves like a pointer; // in the code we'll use "*ebeam". TTreeReaderValue ebeam(myReader, "ebeam"); // For each row in the TTree: while (myReader.Next()) { // ... do something with *ebeam ... } ::: In [STLntupleRead.cxx](https://www.nevis.columbia.edu/~seligman/root-class/files/root-dict/STLntupleRead.cxx) we see a more extended example of {numref}`Listing %s `, using the classes defined in [STL_TrackList.h](https://www.nevis.columbia.edu/~seligman/root-class/files/root-dict/STL_TrackList.h) and the dictionary-generation code in [STLntuple.icc](https://www.nevis.columbia.edu/~seligman/root-class/files/root-dict/STLntuple.icc). If you're running these examples in sequence, note that that if you previously generated the dictionary when running `STLntupleCreate`, `STLntupleRead` will execute quickly, since it won't regenerate the dictionary if {file}`STL_TrackList.h` is not changed. If you went through {ref}`rdataframe`, you may be interested in my test program [STLntupleRDF.cxx](https://www.nevis.columbia.edu/~seligman/root-class/files/root-dict/STLntupleRDF.cxx). Since "upgrading" the `STLntupleRead` code seemed too simple to me, I upgraded my self-appointed exercise to include an additional feature: using `RDataFrame` in multi-threaded mode to accumulate histograms. If you're a Python programmer, you'll want to focus on [STLntupleRead.py](https://www.nevis.columbia.edu/~seligman/root-class/files/root-dict/STLntupleRead.py). This has the identical functionality of `STLntupleRead`. This demonstrates that it only takes a minor "pythonization" of the C++ ROOT code to use dictionaries in Python.