Warning, /tutorial-jana2/episodes/03-end-user-plugin.md is written in an unsupported language. File is not indexed.
0001 ---
0002 title: "Creating a plugin to make custom histograms/trees"
0003 teaching: 15
0004 exercises: 20
0005 ---
0006
0007 ::::::::::::::::::::::::::::::::::::::::::::: questions
0008
0009 - Why should a I make a custom plugin?
0010 - How do I create a custom plugin?
0011
0012 :::::::::::::::::::::::::::::::::::::::::::::
0013
0014 ::::::::::::::::::::::::::::::::::::::::::::: objectives
0015
0016 - Understand when one should make a plugin and when they should just use a ROOT macro.
0017 - Understand how to create a new, stand-alone plugin with your own custom histograms.
0018
0019 :::::::::::::::::::::::::::::::::::::::::::::
0020
0021 ::::::::::::::::::::::::::::::::::::::::::::: callout
0022
0023 Note: The following episode presents a somewhat outdated view, and some commands may not function.
0024 In particular, the `eicmkplugin.py` helper script used below **is no longer shipped with the
0025 `eic-shell` container / EICrecon**, so the `eicmkplugin.py myFirstPlugin` step will not work as
0026 written. For the current, supported way to create and build an EICrecon plugin, follow the plugin
0027 documentation in the [EICrecon repository](https://github.com/eic/EICrecon).
0028
0029 If you are only interested in analyzing already-reconstructed data, then there is no requirement
0030 to use a plugin as described below; **just analyze output ROOT file directly instead**.
0031
0032 :::::::::::::::::::::::::::::::::::::::::::::
0033
0034 Plugins are the basic building blocks when it comes to analyzing data. They request objects and perform actions, such as making histograms, or writing out certain objects to files. When your plugin requests objects (e.g. clusters) the factory responsible for the requested object is loaded and run (We will dive into factories in the next exciting episode of how to use JANA). When running EICrecon you will configure it to use some number of plugins (each potentially with their own set of configuration parameters). Now, let us begin constructing a new plugin.
0035
0036 To do this we will use the eicmkplugin.py script that comes with EICrecon. This utility should be your "go-to" for jumpstarting your work with EICrecon/JANA when it comes to data. To put eicmkplugin.py in your path, you can do the following:
0037
0038 ```bash
0039 source EICrecon/bin/eicrecon-this.sh
0040 ```
0041
0042 The eickmkplugin script can be called simply by typing: "eicmkplugin.py" followed by the name of the plugin. Let's begin by calling:
0043
0044 ```bash
0045 eicmkplugin.py myFirstPlugin
0046 ```
0047
0048 You should now have terminal output that looks like this:
0049
0050 ```output
0051 Writing myFirstPlugin/CMakeLists.txt ...
0052 Writing myFirstPlugin/myFirstPluginProcessor.h ...
0053 Writing myFirstPlugin/myFirstPluginProcessor.cc ...
0054
0055 Created plugin myFirstPlugin.
0056 Build with:
0057
0058 cmake -S myFirstPlugin -B myFirstPlugin/build
0059 cmake --build myFirstPlugin/build --target install
0060 ```
0061
0062 There should now exist a new folder labeled "myPlugin". That directory contains 2 files: a CMakelists.txt file (needed for compiling our new plugin) and the source code for the plugin itself.
0063
0064 Inside the source code for your plugin is a fairly simple class. The private data members should contain the necessary variables to successfully run your plugin; this will likely include any histograms, canvases, fits or other accoutrement. The public section contains the required Constructor, Init, Process, and Finish functions. In init we get the application, as well as initialize any variables or histograms, etc. The Process function typically gets objects from the event and does something with them (e.g. fill the histogram of cluster energy). And finally Finish is called where we clean up and do final things before ending the run of our plugin.
0065
0066 Before we compile our plugins we need to tell JANA about where the plugins will be found. Start by setting your EICrecon_MY environment variable to a directory where you have write permission. The build instructions will install the plugin to that directory. When eicrecon is run, it will also look for plugins in the $EICrecon_MY directory and the EICrecon build you are using. This step is easy to overlook but necessary for the plugin to be found once compiled. Let's do this now before we forget:
0067
0068 ```bash
0069 mkdir EICrecon_MY
0070 export EICrecon_MY=${PWD}/EICrecon_MY
0071 ```
0072
0073 To compile your plugin, let's follow the guidance given and type:
0074
0075 ```bash
0076 cmake -S myFirstPlugin -B myFirstPlugin/build
0077 cmake --build myFirstPlugin/build --target install
0078 ```
0079
0080 You can test plugin installed and can load correctly by runnign eicrecon with it:
0081
0082 ```bash
0083 eicrecon -Pplugins=myFirstPlugin,JTest -Pjana:nevents=10
0084 ```
0085
0086 The second plugin, JTest, just supplies dummy events, ensuring your plugin is properly compiled and found. To generate your first histograms, let's edit the myFirstPluginProcessor.cc and myFirstPluginProcessor.h files (located in the myFirstPlugin directory). Start by modifying myFirstPluginProcessor.h. In the end it should look similar to the one below:
0087
0088 ```c++
0089 #include <JANA/JEventProcessorSequentialRoot.h>
0090 #include <TH2D.h>
0091 #include <TFile.h>
0092
0093 #include <edm4hep/SimCalorimeterHit.h>
0094
0095 class myFirstPluginProcessor: public JEventProcessorSequentialRoot {
0096 private:
0097
0098 // Data objects we will need from JANA e.g.
0099 PrefetchT<edm4hep::SimCalorimeterHit> rawhits = {this, "EcalBarrelHits"};
0100
0101 // Declare histogram and tree pointers here. e.g.
0102 TH1D* hEraw = nullptr;
0103
0104 public:
0105 myFirstPluginProcessor() { SetTypeName(NAME_OF_THIS); }
0106
0107 void InitWithGlobalRootLock() override;
0108 void ProcessSequential(const std::shared_ptr<const JEvent>& event) override;
0109 void FinishWithGlobalRootLock() override;
0110 };
0111 ```
0112
0113 Next, edit the myFirstPluginProcessor.cc file to the following:
0114
0115 ```c++
0116 #include "myFirstPluginProcessor.h"
0117 #include <services/rootfile/RootFile_service.h>
0118
0119 // The following just makes this a JANA plugin
0120 extern "C" {
0121 void InitPlugin(JApplication *app) {
0122 InitJANAPlugin(app);
0123 app->Add(new myFirstPluginProcessor);
0124 }
0125 }
0126
0127 //-------------------------------------------
0128 // InitWithGlobalRootLock
0129 //-------------------------------------------
0130 void myFirstPluginProcessor::InitWithGlobalRootLock(){
0131
0132 // This ensures the histograms created here show up in the same root file as
0133 // other plugins attached to the process. Place them in dedicated directory
0134 // to avoid name conflicts.
0135 auto rootfile_svc = GetApplication()->GetService<RootFile_service>();
0136 auto rootfile = rootfile_svc->GetHistFile();
0137 rootfile->mkdir("myFirstPlugin")->cd();
0138
0139 hEraw = new TH1D("Eraw", "BEMC hit energy (raw)", 100, 0, 0.075);
0140 }
0141
0142 //-------------------------------------------
0143 // ProcessSequential
0144 //-------------------------------------------
0145 void myFirstPluginProcessor::ProcessSequential(const std::shared_ptr<const JEvent>& event) {
0146
0147 for( auto hit : rawhits() ) hEraw->Fill( hit->getEnergy() );
0148 }
0149
0150 //-------------------------------------------
0151 // FinishWithGlobalRootLock
0152 //-------------------------------------------
0153 void myFirstPluginProcessor::FinishWithGlobalRootLock() {
0154
0155 // Do any final calculations here.
0156 }
0157 ```
0158
0159 Before we continue, stop for a moment and remember that plugins are compiled objects. Thus it is imperative we rebuild our plugin after making any changes. To do this, we can simply run the same commands we used to build the plugin in the first place:
0160
0161 ```bash
0162 cmake -S myFirstPlugin -B myFirstPlugin/build
0163 cmake --build myFirstPlugin/build --target install
0164 ```
0165
0166 You can test the plugin using the following simulated data file:
0167
0168 ```bash
0169 wget https://eicaidata.s3.amazonaws.com/2022-09-26_ncdis10x100_minq2-1_200ev.edm4hep.root
0170 eicrecon -Pplugins=myFirstPlugin 2022-09-26_ncdis10x100_minq2-1_200ev.edm4hep.root
0171 ```
0172
0173 You should now have a root file, eicrecon.root, with a single directory: "myFirstPlugin" containing the resulting hEraw histogram.
0174
0175 ::::::::::::::::::::::::::::::::::::::::::::: challenge
0176
0177 ## Exercises
0178
0179 As exercises try (make sure you rebuild everytime you change your plugin):
0180
0181 1. Plot the X,Y positions of all the hits.
0182 2. Repeat only for hits with energy greater than 0.005 GeV.
0183 3. Try to plot similar histograms from the EcalEndcapN.
0184
0185 Feel free to play around with other objects and their properties (hint: when you ran eicrecon, you should have seen a list of all the objects that were available to you. You can also see this list by typing: `eicrecon -Pplugins=myFirstPlugin -Pjana:nevents=0`)
0186
0187 ::::::::::::::: solution
0188
0189 For the X,Y positions, declare a `TH2D` in the header, book it in `InitWithGlobalRootLock`, and fill
0190 it from `hit->getPosition().x` and `hit->getPosition().y` in `ProcessSequential`. Add an
0191 `if (hit->getEnergy() > 0.005)` guard to make the energy-cut version. To switch detectors, change the
0192 `PrefetchT` collection name from `"EcalBarrelHits"` to `"EcalEndcapNHits"`. Rebuild the plugin after
0193 every change with the two `cmake` commands above.
0194
0195 :::::::::::::::
0196
0197 :::::::::::::::::::::::::::::::::::::::::::::
0198
0199 Note: very shortly you will be adding a factory. After you do come back to this plugin and access your newly created objects.
0200
0201 ::::::::::::::::::::::::::::::::::::::::::::: keypoints
0202
0203 - Plugins can be used to generate custom histograms by attaching directly to the reconstruction process.
0204 - Plugins can be used for monitoring or custom analysis.
0205
0206 :::::::::::::::::::::::::::::::::::::::::::::