[Feedback][Tutorial Contents][hep.lcd Home]

shoeline2[1].gif (1488 bytes)

Writing Your Own Analysis Code

In the previous tutorial you used a pre-written analysis module to analyze some LCD data and create histograms. In this tutorial we will show you how to write your own analysis routines, and how to understand the structure of the LCD data.

When you write analysis code in Java Analysis Studio you normally create a new class, such as MyAnalysis which extends a base class called EventAnalyzer. EventAnalyzer is a sort of no-op analysis which provides the framework for doing analysis, but doesn't actually do any specific analysis. By extending it your "MyAnalysis" class automatically inherits all of the framework, but can extend it to actually do some useful analysis. Typically you do this by overriding a single method called processEvent. The processEvent method is called by the framework each time a new event is ready for analysis.

Lets look in detail now at the processEvent method from the previous tutorial, and go through it line by line explaining what is going on.

   public void processEvent(EventData d)
   {
      LCDEvent header = (LCDEvent) d;

The first line declares the processEvent method. It must be public (so the framework can call it) and void (it doesn't return anything). The argument to the processEvent method is the event to be analyzed. Since the Java Analysis Studio is experiment independent, the event passed to the processEvent method is of a generic EventData type, but since we know what type of data we want to analyze (in this case LCD data) we can cast the input object to an LCDEvent.

LCDEvent is defined as part of the hep.lcd classes, and is an event header that occurs in each LCD event. From the LCDEvent class it is possible to navigate to the rest of the event data. There are a lot of classes defined in hep.lcd, but you will not need to refer to most of the classes unless you want to write your own reconstruction modules. To do data analysis you just need to know about the relatively few hep.lcd.event classes, of when LCDEvent is one. You can find detailed documentation on the hep.lcd.event classes here. In the rest of this tutorial we will include a link to the detailed documentation on new classes as we introduce them, like this: LCDEvent(in the API reference documentation).

Continuing with our analysis routine:

      CalorimeterHits hits = header.getEMCalorimeterHits();
      double EMEnergy = sumEnergy(hits.getHits());

The first  line here declares a variable called hits, which is a reference to an object of type CalorimeterHits(in the API reference documentation). This variable is initialized to refer to a CalorimeterHits object, obtained from the LCDEvent by calling the getEMCalorimeterHits() method (which returns the hits in the EM Calorimeter). The second line defines a variable of type double and sets it to the result of calling the sumEnergy routine that we will describe later. Note that it is common practice to always use double for floating point variables since with most computers today the overhead of using double instead of float is trivial.

      histogram("EM nhits").fill(hits.getNHits());
      histogram("EM Energy").fill(EMEnergy);

These two lines produce the first two histograms. Note that it takes only one line to declare and fill a histogram, which makes adding histograms to programs in Java Analysis Studio very straightforward.

In more detail what is happening here is that we are calling a method called histogram which takes a String argument (the histogram name) and returns a Histogram(in the API reference documentation) object, we are then calling the fill method of the Histogram object, which takes a single numeric argument (the value to fill into the histogram).

The histogram method is a framework method that our class inherited from the EventAnalyzer(in the API reference documentation) class. It looks for a named histogram and returns a reference to the corresponding Histogram object. If a Histogram of the given name does not exist it creates a new Histogram and returns that.

      hits = header.getHADCalorimeterHits();
      double HADEnergy = sumEnergy(hits.getHits());

      histogram("HAD nhits").fill(hits.getNHits());
      histogram("HAD Energy").fill(HADEnergy);

      hits = header.getMuonCalorimeterHits();
      histogram("MU nhits").fill(hits.getNHits());
      histogram("MU Energy").fill(sumEnergy(hits.getHits()));

      histogram("Total Energy").fill(EMEnergy/0.017 + HADEnergy/0.071);
   }

The remainder of the processEvent method should be self-explanatory, just repeating what we have already seen for hadron calorimeter hits followed by the muon calorimeterHits, and then finally histogramming the sum of the energy in the EM and HAD calorimeters.

   private double sumEnergy(Enumeration e)
   {
      double energy = 0;
      while (e.hasMoreElements())
      {
         CalorimeterHit hit = (CalorimeterHit) e.nextElement();
         energy += hit.getEnergy();
      }
      return energy;
   }

The remainder of the analysis routine is the definition of the sumEnergy method. The sumEnergy method is only called from within this same class, so it is declared private, and it returns a double. The argument to the routine is an object of type Enumeration(in the API reference documentation). Enumeration is a Java utility class (from the package java.util) which provides a means of looping over a collection of objects without knowing the organization of the collection. Looking back at the places where sumEnergy was called you can see that in each case we obtained an Enumeration of the hits from the appropriate CalorimeterHits collection and passed that as the argument.

The body of the sumEnergy method simply loops over the elements of the Enumeration, using the methods in Enumeration to check if more elements exist, and if so to fetch the next element. Since an Enumeration can contain any type of object, the nextElement method returns an Object, however since we know the collections will in fact consist of CalorimeterHit(in the API reference documentation) objects, we can cast the result and use the CalorimeterHit.getEnergy method to extract the energy of the hit.

Finally lets look at the entire analysis routine to see what boilerplate code we need to make a complete analysis routine.

import hep.analysis.*;
import hep.lcd.event.*;
import java.util.*;

public class LCDResolution extends EventAnalyzer
{
   public LCDResolution()
   {
   }
   public void processEvent(EventData d)
   {
      LCDEvent header = (LCDEvent) d;

      CalorimeterHits hits = header.getEMCalorimeterHits();
      double EMEnergy = sumEnergy(hits.getHits());

      histogram("EM nhits").fill(hits.getNHits());
      histogram("EM Energy").fill(EMEnergy);

      hits = header.getHADCalorimeterHits();
      double HADEnergy = sumEnergy(hits.getHits());

      histogram("HAD nhits").fill(hits.getNHits());
      histogram("HAD Energy").fill(HADEnergy);

      hits = header.getMuonCalorimeterHits();
      histogram("MU nhits").fill(hits.getNHits());
      histogram("MU Energy").fill(sumEnergy(hits.getHits()));

      histogram("Total Energy").fill(EMEnergy/0.017 + HADEnergy/0.071);
   }
   private double sumEnergy(Enumeration e)
   {
      double energy = 0;
      while (e.hasMoreElements())
      {
         CalorimeterHit hit = (CalorimeterHit) e.nextElement();
         energy += hit.getEnergy();
      }
      return energy;
   }
}

The full name of the LCDEvent class (for instance) is hep.lcd.event.LCDEvent. To avoid having to always specify the entire long name each time we use it, we can use the import statement. The import statement is used to import a namespace into the current routine, so that the abbreviated name can be used. In this case we start the routine by importing three namespaces, hep.lcd.event (used for LCDEvent, CalorimeterHits and CalorimeterHit classes), java.util (used for Enumeration) and hep.analysis (used for Histogram and EventAnalyzer).

Next we declare the class itself, in this case called LCDResolution, which as we have explained before extends the framework routine EventAnalyzer. Finally we declare a public constructor for the LCDResolution class which takes no arguments, so that the class can be created by the framework. Constructors always have the same name as the class itself, and have no declared return type. If we needed to do some initialization we could do it in the body of the class constructor, but in this case there is no initialization needed, so the constructor body is empty.

More Information

The remaining tutorials in this series contain several more examples of writing analysis routines. Other resources that may be useful: