Back to home page

EIC code displayed by LXR

 
 

    


Warning, /jana2/docs/tutorial.md is written in an unsupported language. File is not indexed.

0001 # Tutorial
0002 
0003 This tutorial will walk you through creating a standalone JANA plugin, introducing the key ideas along the way. 
0004 The end result for this example is available under 
0005 [src/examples/Tutorial](https://github.com/JeffersonLab/JANA2/tree/master/src/examples/Tutorial).
0006 
0007 ## Introduction
0008 
0009 Before we begin, we need to make sure that 
0010 * The JANA library is installed
0011 * The `JANA_HOME` environment variable points to the installation directory
0012 * Your `$PATH` contains `$JANA_HOME/bin`. 
0013 
0014 The installation process is described [here](install.md). We can quickly test that our install 
0015 was successful by running a builtin benchmarking/scaling test:
0016 
0017 ```
0018 jana -Pplugins=JTest -b   # (cancel with Ctrl-C)
0019 ```
0020 
0021 We can understand this command as follows:
0022 
0023 * `jana` is the default command-line tool for launching JANA. If you would rather create your own executable which 
0024    uses JANA internally, you are free to do so.
0025    
0026 * The `-P` flag specifies a configuration parameter, e.g. `-Pjana:debug_plugin_loading=1` tells JANA to log
0027    detailed information about where the plugin loader went looking and what it found.
0028    
0029 * `plugins` is the parameter specifying the names of plugins to load, as a comma-separated list (without spaces). 
0030 By default JANA searches for these in `$JANA_HOME/plugins`, although you can also specify full paths.
0031 
0032 * `-b` tells JANA to run everything in benchmark mode, i.e. it slowly increases the number of threads while 
0033 measuring the overall throughput. You can cancel processing at any time by pressing Ctrl-C.
0034 
0035 
0036 ## Creating a JANA plugin
0037 
0038 With JANA working, we can now create our own plugin. JANA provides a script which generates code skeletons 
0039 to help us get started. We shall generate a skeleton for a plugin named "QuickTutorial" as follows:
0040 ```
0041 jana-generate.py Plugin QuickTutorial
0042 ```
0043 
0044 This creates the following directory tree. By default, a minimal skelton is created in a single file:
0045 `QuickTutorial.cc`. This provides a JEventProcessor class as well as the the plugin entry point. The
0046 generated files include lots of comments providing helpful hints on their use. 
0047 
0048 ```
0049 QuickTutorial/
0050 ├── CMakeLists.txt
0051 │├─ QuickTutorial.cc
0052 ```
0053 
0054 The `jana-generate.py Plugin ...` command provides some option flags as well that can be given at the 
0055 end of the command line. Run `jana-generate.py --help` to see what they are.
0056 
0057 ## Integrating into an existing project
0058 
0059 If you are working with an existing project such as eJANA or GlueX, then you don't need the CMake
0060 project. All you need are the source files (e.g. `QuickTutorial.cc`):
0061 ```
0062 cp QuickTutorial $PATH_TO_PROJECT_SOURCE/src/plugins/QuickTutorial
0063 ```
0064 
0065 Be aware that you will have to manually tell the parent CMakeLists.txt to 
0066 `add_subdirectory(QuickTutorial)`.
0067 
0068 The rest of the tutorial assumes that we are using a standalone plugin.
0069 
0070 
0071 ## Building the plugin
0072 We build and run the plugin with the following:
0073 
0074 ```
0075 cd QuickTutorial
0076 mkdir build
0077 cd build
0078 cmake3 ..
0079 make install
0080 jana -Pplugins=QuickTutorial
0081 ```
0082 
0083 ## Adding an event source
0084 
0085 When we run this, we observe that JANA loads the plugin, opens our QuickTutorialProcessor, closes it 
0086 again without processing any events, and exits. This is because there is nothing to do because we haven't
0087 specified any sources. If we are running in the context of an existing project, we can pull in event sources
0088 from other plugins and observe our processor dutifully print out the event number. For now, however, we 
0089 assume that we don't have access to an event source, so we'll create one ourselves. Our first event
0090 source will emit an infinite stream of random data, so we'll name it RandomSource.
0091 
0092 ```
0093 cd ..
0094 jana-generate.py JEventSource RandomSource
0095 ```
0096 
0097 This creates two files, `RandomSource.cc` and `RandomSource.h`, in the current directory. We'll
0098 need to add them to `CMakeLists.txt` ourselves. Note that we retain complete control over our directory 
0099 structure. In this tutorial, for simplicity, we'll keep all .h and .cc files in the topmost directory.
0100 For larger projects, `jana-generate project MyProjectName` creates
0101 a much more complex code skeleton. 
0102 
0103 To use our new RandomSource as-is, we need to do three things:
0104 * Add `RandomSource.cc` and `RandomSource.h` to the `add_library(...)` line in `CMakeLists.txt`.
0105 * Register our `RandomSource` with JANA inside `QuickTutorial.cc`
0106 * Rebuild the cmake project, rebuild the plugin target, and install.
0107 
0108 The modified line in the CMakeLists.txt line should look like:
0109 ```
0110 add_library(QuickTutorial_plugin SHARED QuickTutorial.cc RandomSource.cc RandomSource.h)
0111 ```
0112 
0113 The modified `QuickTuorial.cc` file needs to have the new `RandomSource.h` header included so
0114 it can instantiatie an object and pass it over to the JApplication in the `InitPlugin()` routine.
0115 The bottom of the file should look like this: 
0116 
0117 ```
0118 #include <RandomSource.h>                             // <- ADD THIS LINE (probably better to put this at top of file)
0119 
0120 extern "C" {
0121     void InitPlugin(JApplication *app) {
0122         InitJANAPlugin(app);
0123         app->Add(new QuickTutorialProcessor);
0124         app->Add(new RandomSource);    // <- ADD THIS LINE
0125     }
0126 }
0127 ```
0128 
0129 And finally, rebuild ...
0130 ```
0131 cd build
0132 make install
0133 ```
0134 
0135 When we run the QuickTutorial plugin now, we observe that `QuickTutorialProcessor::Process`
0136 is being called on every event. Note that `Process` is 'seeing' events slightly out-of-order. This is 
0137 because there are multiple threads running `Process`, which means that we have to be careful about how 
0138 we organize the work we do inside there. This will be discussed in depth later.
0139 
0140 ## Configuring an event source
0141 
0142 Because neither the source nor the processor are doing any 'real work', the events are being processed 
0143 very quickly. To throttle the rate events get emitted, to whatever frequency we like, we can add a delay 
0144 inside `GetEvent`. Perhaps we'd even like to set the emit frequency at runtime. First, we declare a member variable on 
0145 `RandomSource`, initializing it to our preferred default value:
0146 
0147 ```
0148 class RandomSource : public JEventSource {
0149     int m_max_emit_freq_hz = 100;             // <- ADD THIS LINE
0150 
0151 public:
0152     RandomSource();
0153     RandomSource(std::string resource_name, JApplication* app);
0154     virtual ~RandomSource() = default;
0155     void Open() override;
0156     void Close() override;
0157     Result Emit(JEvent& event) override;
0158     static std::string GetDescription();
0159 };
0160 ```
0161 
0162 Next we sync the variable with the parameter manager inside `Open`. We do this by calling 
0163 `JApplication::SetDefaultParameter`, which tells JANA to look among its configuration parameters for one 
0164 called "random_source:max_emit_freq_hz". If it finds one, it 
0165 sets `m_max_emit_freq_hz` to the value it found. Otherwise, it leaves the variable alone. JANA remembers 
0166 all such 'default parameters' along with their default values so that it can report them and generate config
0167 files. Note that we conventionally prefix our parameter names with the name of the requesting component or 
0168 plugin. This helps prevent namespace collisions. 
0169 
0170 ```
0171 void RandomSource::Open() {
0172     JApplication* app = GetApplication();                                                                       // <- ADD THIS LINE
0173     app->SetDefaultParameter("random_source:max_emit_freq_hz",            // <- ADD THIS LINE
0174                              m_max_emit_freq_hz,                          // <- ADD THIS LINE
0175                              "Maximum event rate [Hz] for RandomSource"); // <- ADD THIS LINE
0176 }
0177 ```
0178 
0179 We can now use the value of `m_max_emit_freq_hz`, confident that it is consistent with the current 
0180 runtime configuration:
0181 
0182 ```
0183 JEventSource::Result RandomSource::Emit(JEvent& event) {
0184 
0185     /// Configure event and run numbers
0186     static size_t current_event_number = 1;
0187     event.SetEventNumber(current_event_number++);
0188     event.SetRunNumber(22);
0189 
0190     /// Slow down event source                                           // <- ADD THIS LINE
0191     auto delay_ms = std::chrono::milliseconds(1000/m_max_emit_freq_hz);  // <- ADD THIS LINE
0192     std::this_thread::sleep_for(delay_ms);                               // <- ADD THIS LINE
0193 
0194     return Result::Success;
0195 }
0196 ```
0197 
0198 Finally, we can set this parameter on the command line and observe the throughput change accordingly:
0199 
0200 ```
0201 jana -Pplugins=QuickTutorial -Prandom_source:max_emit_freq_hz=10
0202 ```
0203 
0204 
0205 ## Creating JObjects
0206 
0207 So far `RandomSource` has been emitting events with no data attached. Now we'd like to have them 
0208 emit randomly generated 'Hit' objects which simulate the readout from a detector. First, we need 
0209 to set up our data model. Although we can insert pointers of any kind into our `JEvent`, we strongly
0210 recommend using `JObjects` for reasons we will discuss later. 
0211 
0212 ```
0213 cd src
0214 jana-generate.py JObject Hit
0215 ```
0216 
0217 JObjects are meant to be plain-old data. For this tutorial we pretend that our detector consists of a 3D grid
0218 of sensors, each of which measures some energy at some time. Note that we are declaring `Hit` to be a `struct`
0219 instead of a `class`. This is because `JObjects` should be lightweight containers with no creation logic and 
0220 no invariants which need to be encapsulated. JObjects are free to contain pointers to arbitrary data types and
0221 nested STL containers, but the recommended approach is to maintain a flat structure of primitives whenever possible.
0222 A JObject should conceptually resemble a row in a database table.
0223 
0224 ```
0225 struct Hit : public JObject {
0226     int x;     // Pixel coordinates
0227     int y;     // Pixel coordinates
0228     double E;  // Energy loss in GeV
0229     double t;  // Time in us
0230 
0231     // Make it possible to construct a Hit as a one-liner
0232     Hit(int x, int y, double E, double t) : x(x), y(y), E(E), t(t) {};
0233     ...
0234 ```
0235 
0236 The only additional thing we need to fill out is the `Summarize` method, which aids in debugging and introspection.
0237 Basically, it tells JANA how to convert this JObject into a (structured) string. Inside `Summarize`, we add each of 
0238 our primitive member variables to the provided `JObjectSummary`, along with the variable name, a C-style format 
0239 specifier, and a description of what that variable means. JANA provides a `NAME_OF` macro so that if we rename a 
0240 member variable using automatic refactoring tools, it will automatically update the string representation of the variable 
0241 name as well. 
0242 
0243 ```
0244     ...
0245     void Summarize(JObjectSummary& summary) const override {
0246         summary.add(x, NAME_OF(x), "%d", "Pixel coordinates centered around 0,0");
0247         summary.add(y, NAME_OF(y), "%d", "Pixel coordinates centered around 0,0");
0248         summary.add(E, NAME_OF(E), "%f", "Energy loss in GeV");
0249         summary.add(t, NAME_OF(t), "%f", "Time in us");
0250     }
0251 }
0252 ```
0253 
0254 ## Inserting JObjects into a JEvent
0255 
0256 Now it is time to have our `RandomSource` emit events which contain `Hit` objects. For the sake
0257 of brevity, we shall keep our hit generation logic as simple as possible: four hits which are constant.
0258 We can make our detector simulation arbitrarily complex, but be aware that `JEventSources` only run on
0259 a single thread by default, so complex simulations can reduce the event rate. Synchronizing `GetEvent` makes our job 
0260 easier, however, because we can manipulate non-thread-local state such as file pointers or cursors or message buffers 
0261 without having to worry about race conditions and deadlocks.
0262 
0263 The pattern we use for inserting data into the event is simple: For data of type `T`, create a `std::vector<T*>`, fill
0264 it, and pass it to `JEvent::Insert`, which will move its contents directly into the `JEvent` object. If we want,
0265 when we insert we can also specify a tag, which is just a string. The purpose of a tag is to provide an extra level
0266 of granularity. For instance, if we have two detectors which both use the `Hit` datatype but have separate processing
0267 logic, we want to be able to access them independently.
0268 
0269 ```
0270 #include "Hit.h"
0271 // ...
0272 
0273 Result RandomSource::Emit(JEvent& event) {
0274     // ...
0275 
0276     /// Insert simulated data into event       // ADD ME
0277 
0278     std::vector<Hit*> hits;                    // ADD ME
0279     hits.push_back(new Hit(0, 0, 1.0, 0));     // ADD ME
0280     hits.push_back(new Hit(0, 1, 1.0, 0));     // ADD ME
0281     hits.push_back(new Hit(1, 0, 1.0, 0));     // ADD ME
0282     hits.push_back(new Hit(1, 1, 1.0, 0));     // ADD ME
0283 
0284     event.Insert(hits);                       // ADD ME
0285     //event.Insert(hits, "fcal");             // If we used a tag
0286 
0287     return Result::Success;
0288 }
0289 ```
0290 
0291 
0292 ## Writing our own JEventProcessor
0293 
0294 A JEventProcessor does two things: It calculates a bunch of intermediate results
0295 for each event (this part is done in parallel), and then it aggregates those results 
0296 into a single output (this part is done sequentially). The canonical example is to 
0297 calculate clusters, track candidates, and tracks separately for each event, and then 
0298 produce a histogram using all of the tracks of all of the events.
0299 
0300 In this section, we are going to modify the automatically generated `TutorialProcessor` to 
0301 produce a heatmap that only uses hit data. We discuss how to structure more complicated 
0302 calculations later. First, we add a quick-and-dirty heatmap member variable:
0303 
0304 ```
0305 class QuickTutorialProcessor : public JEventProcessor {
0306     double m_heatmap[100][100];     // ADD ME
0307     std::mutex m_mutex;
0308 
0309 public:
0310     // ...
0311 ```
0312 
0313 The heatmap itself is a piece of _shared_ _state_. We have to be careful because if 
0314 multiple threads try to read and write to this shared state, they will conflict with each 
0315 other and corrupt it. This means we have to protect _who_ can access it and _when_. 
0316 Only QuickTutorialProcessor should be able to access it, so we make it a private member.
0317 However, this is not enough. Only one thread running `QuickTutorialProcessor::Process` must
0318 be allowed to access it at a time, which we enforce using `m_mutex`. Let's look at how this
0319 is used:
0320 
0321 ```
0322 #include "Hit.h"                                // ADD ME
0323 
0324 void QuickTutorialProcessor::Process(const std::shared_ptr<const JEvent> &event) {
0325 
0326     /// Do everything we can in parallel
0327     /// Warning: We are only allowed to use local variables and `event` here
0328     auto hits = event->Get<Hit>();              // ADD ME
0329     
0330     /// Lock mutex
0331     std::lock_guard<std::mutex>lock(m_mutex);
0332 
0333     /// Do the rest sequentially
0334     /// Now we are free to access shared state such as m_heatmap
0335     for (const Hit* hit : hits) {               // ADD ME
0336         m_heatmap[hit->x][hit->y] += hit->E;    // ADD ME
0337     }
0338 }
0339 ```
0340 
0341 As you can see, we do everything we can in parallel, before we lock our mutex. All we are 
0342 doing for now is retrieve the `Hit` objects we `Insert`ed earlier, however, as we will later
0343 see, virtually all of our per-event computations will be called from here. Remember that
0344 we should _only_ access local variables and data retrieved from a `JEvent` at first, whereas 
0345 after we lock the mutex, we are free to access our private member variables as well.
0346 
0347 We proceed to define our `Init` and `Finish` methods. The former zeroes out each bucket and
0348 the latter prints the heatmap to standard out as ASCII art. Note that if we want to output 
0349 our results to a file all at once, we should do so in `Finish`. `Finish` will be called even 
0350 if we forcibly terminate JANA with Ctrl-C. On the other hand, if we wanted to write to a file
0351 incrementally, we can open it in `Init`, access it `Process`, and close it in `Finish`.
0352 
0353 ```
0354 void QuickTutorialProcessor::Init() {
0355     LOG << "QuickTutorialProcessor::Init: Initializing heatmap" << LOG_END;
0356 
0357     for (int i=0; i<100; ++i) {
0358         for (int j=0; j<100; ++j) {
0359             m_heatmap[i][j] = 0.0;
0360         }
0361     }
0362 }
0363 
0364 void QuickTutorialProcessor::Finish() {
0365     LOG << "QuickTutorialProcessor::Finish: Displaying heatmap" << LOG_END;
0366 
0367     double min_value = m_heatmap[0][0];
0368     double max_value = m_heatmap[0][0];
0369 
0370     for (int i=0; i<100; ++i) {
0371         for (int j=0; j<100; ++j) {
0372             double value = m_heatmap[i][j];
0373             if (min_value > value) min_value = value;
0374             if (max_value < value) max_value = value;
0375         }
0376     }
0377     if (min_value != max_value) {
0378         char ramp[] = " .:-=+*#%@";
0379         for (int i=0; i<100; ++i) {
0380             for (int j=0; j<100; ++j) {
0381                 int shade = int((m_heatmap[i][j] - min_value)/(max_value - min_value) * 9);
0382                 std::cout << ramp[shade];
0383             }
0384             std::cout << std::endl;
0385         }
0386     }
0387 }
0388 ```
0389 
0390 ## Organizing computations using JFactories
0391 
0392 Just as JANA uses JObjects to organize experiment data, it uses JFactories to organize the algorithms for processing said data.
0393 
0394 JFactories are slightly different from the 'Factory' design patterns: rather than abstracting away the subclass of the 
0395 object being constructed, JFactories abstract away the multiplicity instead. This is a good match for nuclear and 
0396 high-energy physics, where m inputs produce n outputs and n isn't always known until after the algorithm has finished. 
0397 JFactories confer other benefits as well:
0398 
0399 - Algorithms can be swapped at runtime
0400 - Results are calculated only if they are needed ('lazy')
0401 - Results are only calculated once and then reused as needed ('memoized')
0402 - JFactories are agnostic as to whether their inputs were calculated by another JFactory or inserted by a JEventSource
0403 - Different paths for deriving a result may come into play depending on the source data
0404 
0405 For this example, we create a simple algorithm computing clusters, given hit data. We start by generating a cluster 
0406 JObject:
0407 
0408 ```jana-generate.py JObject Cluster```
0409 
0410 We fill out the `Cluster.h` skeleton, defining a cluster to be the coordinates of its center along with the total energy 
0411 and time interval. Note that using JObjects helps keep our domain model malleable, so we can evolve it over
0412 time as we learn more. 
0413 
0414 ```
0415 struct Cluster : public JObject {
0416     double x_center;     // Pixel coordinates centered around 0,0
0417     double y_center;     // Pixel coordinates centered around 0,0
0418     double E_tot;     // Energy loss in GeV
0419     double t_begin;   // Time in us
0420     double t_end;     // Time in us
0421 
0422     Cluster(double x_center, double y_center, double E_tot, double t_begin, double t_end)
0423         : x_center(x_center), y_center(y_center), E_tot(E_tot), t_begin(t_begin), t_end(t_end) {};
0424 
0425     void Summarize(JObjectSummary& summary) const override {
0426         summary.add(x_center, NAME_OF(x_center), "%f", "Pixel coords <- [0,80)");
0427         summary.add(y_center, NAME_OF(y_center), "%f", "Pixel coords <- [0,24)");
0428         summary.add(E_tot, NAME_OF(E_tot), "%f", "Energy loss in GeV");
0429         summary.add(t_begin, NAME_OF(t_begin), "%f", "Earliest observed time in us");
0430         summary.add(t_end, NAME_OF(t_end), "%f", "Latest observed time in us");
0431     }
0432 ...
0433 }
0434 ```
0435 
0436 Now we generate a JFactory which will compute n `Cluster`s given m `Hit`s. Note that
0437 we need to provide both the classname of our factory and the classname of the JObject
0438 it produces.
0439 
0440 ```jana-generate.py JFactory SimpleClusterFactory Cluster```
0441 
0442 The heart of a JFactory is the function `Process`, where we take an event, extract
0443 whatever inputs we need by calling `JEvent::Get` or one of its variants, produce 
0444 some number of outputs, and publish them by calling `JFactory::Set`. These outputs
0445 will stay cached as long as the current event is in flight and get cleared afterwards.
0446 To keep things really simple, our example shall assume there is only one cluster and all of the
0447 hits associated with this event belong to it.
0448 
0449 ```
0450 #include "Hit.h"
0451 // ...
0452 
0453 void SimpleClusterFactory::Process(const std::shared_ptr<const JEvent> &event) {
0454 
0455     auto hits = event->Get<Hit>();
0456 
0457     auto cluster = new Cluster(0,0,0,0,0);
0458     for (auto hit : hits) {
0459         cluster->x_center += hit->x;
0460         cluster->y_center += hit->y;
0461         cluster->E_tot += hit->E;
0462         if (cluster->t_begin > hit->t) cluster->t_begin = hit->t;
0463         if (cluster->t_end < hit->t) cluster->t_end = hit->t;
0464     }
0465     cluster->x_center /= hits.size();
0466     cluster->y_center /= hits.size();
0467 
0468     std::vector<Cluster*> results;
0469     results.push_back(cluster);
0470     Set(results);
0471 }
0472 ```
0473 
0474 For our tutorial, we don't need to do anything inside `Init` or `ChangeRun`. Usually,
0475 these are useful for collecting statistics, or when the algorithm depends on calibration 
0476 constants which we want to cache. We are free to access member variables without locking
0477 a mutex because a JFactory is assigned to at most one thread at a time.
0478 
0479 Although JFactories are relatively simple, there are several important details.
0480 First, because each instance is assigned at most one thread, it won't see the entire event stream. 
0481 Second, there will be at least as many instances of each JFactory in existence as 
0482 threads, and possibly more depending on how JANA is configured, so `Initialize` and 
0483 `ChangeRun` should be fast. Thirdly, although it is tempting to use static variables 
0484 to share state between different instances of the same JFactory, this practice is 
0485 discouraged. That state should live in a JService instead.
0486 
0487 Next, we register our `SimpleClusterFactory` with our JApplication. Because JANA will
0488 need arbitrarily many instances of these, we pass in a `JFactoryGenerator` which
0489 knows how to create a `SimpleClusterFactory`. As long as our JFactory has a zero-argument
0490 constructor, this is easy: 
0491 
0492 ```
0493 #include <JANA/JFactoryGenerator.h>                         // ADD ME
0494 #include "SimpleClusterFactory.h"                            // ADD ME
0495 // ...
0496 
0497 extern "C" {
0498 void InitPlugin(JApplication* app) {
0499 
0500     InitJANAPlugin(app);
0501 
0502     app->Add(new QuickTutorialProcessor);
0503     app->Add(new RandomSource);
0504     app->Add(new JFactoryGeneratorT<SimpleClusterFactory>);  // ADD ME
0505 }
0506 }
0507 ```
0508 
0509 We are now free to modify `QuickTutorialProcessor` (or create a new
0510 `JEventProcessor`) which histograms clusters instead of hits. Crucially,
0511 `JEvent::Get` doesn't care whether the `JObjects` were `Insert`ed by an event
0512 source or whether they were `Set` by a `JFactory`. The interface for retrieving
0513 them is the same either way.
0514 
0515 ## Reading files using a JEventSource
0516 
0517 Earlier we created a `JEventSource` which we added directly to the 
0518 `JApplication`. This works well for simple cases but becomes cumbersome due to 
0519 the amount of configuration needed: First we'd have to tell the plugin which
0520 `JEventSource` to register, then tell that source which files to open, and we'd have
0521 to do this for each `JEventSource` separately. Instead, JANA gives us a cleaner option tailored 
0522 to our workflow: we specify a set of input URIs (a.k.a. file paths or sockets) and let JANA decide
0523 which JEventSource to instantiate for each. Thus we prefer to call JANA like this:
0524 
0525 ```
0526 jana -PQuickTutorial,CsvSourcePlugin,RootSourcePlugin path/to/file1.csv path/to/file2.root
0527 ```
0528 
0529 In order to make this happen, we need to define a `JEventSourceGenerator`. This is conceptually
0530 similar to the `JFactoryGenerator` we mentioned earlier, with one important addition: a method which 
0531 reports back the likelihood that the underlying event source can make sense of that resource. 
0532 Let's remove the line where we added the `RandomSource` instance directly to the JApplication, and replace it with
0533 a corresponding `JEventSourceGenerator`:
0534 
0535 
0536 ```
0537 #include <JANA/JApplication.h>
0538 #include <JANA/JFactoryGenerator.h>
0539 #include <JANA/JEventSourceGeneratorT.h>                    // ADD ME
0540 
0541 #include "Hit.h"
0542 #include "RandomSource.h"
0543 #include "QuickTutorialProcessor.h"
0544 #include "SimpleClusterFactory.h"
0545 
0546 extern "C" {
0547 void InitPlugin(JApplication* app) {
0548 
0549     InitJANAPlugin(app);
0550 
0551     app->Add(new QuickTutorialProcessor);
0552     // app->Add(new RandomSource);           // REMOVE ME
0553     app->Add(new JEventSourceGeneratorT<RandomSource>);     // ADD ME
0554     app->Add(new JFactoryGeneratorT<SimpleClusterFactory>);
0555 }
0556 }
0557 ```
0558 
0559 By default, `JEventSourceGeneratorT` will report a confidence of 0.1 that it can open
0560 any resource it is given. Let's make this more realistic: suppose we want to use this 
0561 event source if and only if the resource name is "random". In `RandomSource.h`, observe
0562 that `jana-generate.py` already declared for us:
0563 
0564 ```
0565 template <>
0566 double JEventSourceGeneratorT<RandomSource>::CheckOpenable(std::string);
0567 ```
0568 
0569 We fill out the definition in `RandomSource.cc`:
0570 
0571 ```
0572 template <>
0573 double JEventSourceGeneratorT<RandomSource>::CheckOpenable(std::string resource_name) {
0574     return (resource_name == "random") ? 1.0 : 0.0;
0575 }
0576 ```
0577 
0578 Note that `JEventSourceGenerator` puts some constraints on our `JEventSource`. Specifically,
0579 we need to note that:
0580 
0581 - Our `JEventSource` needs a two-argument constructor which accepts a string containing 
0582 the resource name, and a `JApplication` pointer.
0583  
0584 - Our `JEventSource` needs a static method `GetDescription`, to help JANA report to the user which sources 
0585 are available and which ended up being chosen.
0586 
0587 - In case we need to override JANA's preferred JEventSource for some resource, we can specify the 
0588 typename of the event source we'd rather use instead via the configuration parameter `event_source_type`.
0589 
0590 - When we implement `Open` for an event source that reads a file, we get the filename
0591 from `JEventSource::GetResourceName()`.
0592 
0593 
0594 ## Exercises for the reader
0595 
0596 - Create a new `JEventProcessor` which generates a heatmap of `Clusters` instead of `Hits`. 
0597 
0598 - Create a `BetterClusterFactory` which handles multiple clusters per event. Bonus points if 
0599   it is a lightweight wrapper around an industrial-strength clustering algorithm.
0600   Inside `InitPlugin`, use a configuration parameter to decide which `JFactoryT<Cluster>`
0601   gets registered with the `JApplication`.
0602 
0603 - Use tags to register both `ClusterFactories` with the `JApplication`. Create a 
0604   `JEventProcessor` which asks for the results from both algorithms and compares their results.
0605 
0606 
0607