Home : Map : Chapter 12 : Java : Tech : Physics :
Chapter 12: JAIDA Demonstration Program
JavaTech
Course Map
Chapter 12

Printing
  Demo 1
Cursor Icons
  Demo 2
MouseButtons
  Demo 3
PopupMenu
  Demo 4
Keystrokes
  Demo 5
Audio
  Demo 6
Timing & Speed
  Demo 7
Toolkit
  Demo 8
AppletContext
  Demo 9
Exercises

    Supplements
Java Beans
More APIs
Java & Browsers
  Demo 1
     About JavaTech
     Codes List
     Exercises
     Feedback
     References
     Resources
     Tips
     Topic Index
     Course Guide
     What's New

The JAIDA (and the corresponding C++ AIDA) library was developed in the High Energy Physics (HEP) community and parts of it, such as the HBOOK style histograms, can be traced back through two or three decades of program development at places like the CERN and SLAC accelerator facilities. For most of that time the programs were written in Fortran but now a pure Java version is available. (Until recently the Minuit fitting tools were still only available in native code but now the JMinuit.jar holds a Java version.) JAIDA is provided via the FreeHEP project.

Though it was developed with HEP in mind, the JAIDA tools can certainly be used for other types of technical programming.

See also the JAS3 (Java Analysis Station v.3) program. It uses the JAIDA classes to provide a powerful standalone general analysis environment. See the documentation and help info for more about this tool.

As an example of using JAIDA classes with your own programs and n using a third-party physics analysis library in general, we created a program to carry out fits to data points with a polynomial. It is very similar to the program described in Chapter 8: Physics : LSQ Fit to a Polynominal but with the JAIDA tools for plotting and fitting.

Since there are a number of JAR files needed, we make the program just an application rather than an applet. To run PolyFitJAIDAApp, you will need to install the JAIDA packages on your system. Go to the java.freehep.org/jaida/ website and follow the instructions for downloading the JAIDA zip file and unpacking it into a convenient location.

To run the program from a command line, go to the JAIDA "bin" subdirectory and run the aida-setup command file appropriate for your platform. For example, on a Windows machine you would run aida-setup.bat as follows:

> C:\Java\MathScience\JAIDA\JAIDA-3.2.4\bin\aida-setup
> set CLASSPATH=.;%CLASSPATH%;

This command file will define the CLASSPATH environment variable so that the compiler and JVM will find the JAIDA packages. The second command adds the current directory to the classpath. The CLASSPATH variable will then go as :

CLASSPATH=.;
C:\Java\MathScience\JAIDA\JAIDA-3.2.4\bin\..\lib\optimizers.jar;
C:\Java\MathScience\JAIDA\JAIDA-3.2.4\bin\..\lib\openide-lookup.jar;
C:\Java\MathScience\JAIDA\JAIDA-3.2.4\bin\..\lib\JMinuit.jar;
C:\Java\MathScience\JAIDA\JAIDA-3.2.4\bin\..\lib\jel.jar;
C:\Java\MathScience\JAIDA\JAIDA-3.2.4\bin\..\lib\jas-plotter.jar;
C:\Java\MathScience\JAIDA\JAIDA-3.2.4\bin\..\lib\freehep-hep.jar;
C:\Java\MathScience\JAIDA\JAIDA-3.2.4\bin\..\lib\freehep-base.jar;
C:\Java\MathScience\JAIDA\JAIDA-3.2.4\bin\..\lib\bcel.jar;
C:\Java\MathScience\JAIDA\JAIDA-3.2.4\bin\..\lib\aida.jar;
C:\Java\MathScience\JAIDA\JAIDA-3.2.4\bin\..\lib\aida-dev.jar;

(This would actually be one long line but line breaks were added here for clarity.)

After the above setup steps, move the command window to the directory with the program, e.g.

> cd C:\Java\MathScience\JAIDA\MyExamples\

And compile the program with:

> javac PolyFitJAIDAApp.java

Then run the program with

> java PolyFitJAIDAApp

As shown in the image below, the program will open with the frame that holds similar text fields and buttons as the Chapter 8 program.. Clicking on "Go" will cause the JAIDA plotter frame to open and display the data points, a polynominal fitted through the points, a histogram showing a distribution of the residuals, and a Gaussian fit to the distribution. The results of the fit are shown in text boxes on each plot.

Clicking on "Go" repeatedly (or putting a larger number in the first data field and clicking once) will produced more fitted lines and the residuals histogram will gradually become more populated and the Gaussian fit will improve and eventually come to match well with the sigma value put in the second data field for the smearing of the data points. Hit "Clear" to clear the histogram and start over, e.g. with a new smearing sigma in the second text field.

Look over the code and then we will give a brief discussion of the JAIDA code used in the program.


PolyFitJAIDAApp

PolyFitJAIDAApp - Create data points in X vs Y and fit a polynominal to them. Use the JAIDA classes for the plotting and the fitting tasks.

import hep.aida.*;
import hep.aida.ref.plotter.PlotterUtilities;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;

/**
  *
  *  This program combines PolyFitApplet with JAIDAEmbed
  *  to illustrate how to use JAIDA classes in your own
  *  GUI physics analysis program. Here we make it a pure
  *  app rather than an applet/app combo.
  *
  *  The program generates points along a quadratic curve and
  *  then fits a polynomial to them. This simulates typical
  *  physics analysis tasks such as fitting particle tracks
  *  through points provided by a wire detector.
  *
  *  The number of curves and the sigma of the smearing of the track
  *  measurement errors are taken from entries in two text fields.
  *  A  histogram holds the residuals
  *
  *  The "Go" button starts the track generation and fitting in a
  *  thread. "Clear"  button clears the histograms.
  *  In standalone mode, the Exit button closes the program.
  *
 **/
public class PolyFitJAIDAApp extends JPanel
             implements ActionListener, Runnable
{

  // Define the various JAIDA instance variables
  IAnalysisFactory fAnalFactory;
  IDataPointSetFactory fDpsFactory;

  // The plotting tool.
  IPlotter fPlotter;

  // The data points to be plotted and fitted with a polynominal
  IDataPointSet fDps2D;

  // Functions for fitting the data points
  // and the residual distribution
  IFunction fP2Func;
  IFunction fGaussFunc;

  // Fitter variables
  IFitter fFitter;
  IFitData fFitData;
  IFitResult fFittedP;
  IFitResult fFittedG;


  // The histograms to record differences between
  // generated tracks and fitted tracks.
  IHistogram1D fResidualsHist;

  // Flag to indicate whether a plot has been made yet.
  boolean fFirstPlot = true;


  // Number of data plots to generate
  int fNumCurves = 1;

  // Scale of the data in x and y.
  double fYMin   =   0.0;
  double fYMax   =  10.0;
  double fXMin   =   0.0;
  double fXMax   = 100.0;

  // Arrays to hold data.
  double [] fX    = new double[20];
  double [] fY    = new double[20];
  double [] fYErr = new double[20];

  // Random number generator
  java.util.Random fRan;

  // Sigma to use for smearing of the points to fit.
  double fCurveSmear = 0.5;

  // Inputs for the number of tracks to generate
  JTextField fNumCurvesField;
  // and the smearing of the tracking points.
  JTextField fSmearField;

  //Buttons
  JButton fGoButton;
  JButton fClearButton;
  JButton fExitButton;

  // Use thread reference as flag.
  Thread fThread;

  /**  Creates a new instance of PolyFitJAIDAApp. The class
    *  extends JPanel. The main() method will create an
    *  instance of this class and add it to a JFrame.
   **/
  public PolyFitJAIDAApp () {
    super(new BorderLayout());
    // Build the GUI
    init ();

  } // ctor

  /**
    * Create a User Interface with histograms and buttons to
    * control the program. Two text files hold number of tracks
    * to be generated and the measurement smearing.
   **/
  public void init () {

    // Create the JAIDA factory objects
    fAnalFactory           = IAnalysisFactory.create ();
    ITree tree             = fAnalFactory.createTreeFactory ().create ();
    fDpsFactory            = fAnalFactory.createDataPointSetFactory (tree);
    IHistogramFactory hf   = fAnalFactory.createHistogramFactory (tree);
    IFunctionFactory funcF = fAnalFactory.createFunctionFactory (tree);
    IFitFactory       fitF = fAnalFactory.createFitFactory ();

    // Create a fitter
    fFitter  = fitF.createFitter ("Chi2","jminuit","noClone=true");
    fFitData = fitF.createFitData ();

    // Create a two dimensional IDataPointSet.
    fDps2D = fDpsFactory.create ("dps2D","two dimensional IDataPointSet",2);

    //Create a 1d second order polynomial
    fP2Func =
      funcF.createFunctionFromScript ("p2", 1, "a+b*x[0]+c*x[0]*x[0]",
                                      "a,b,c", "", null);

    // Create Gaussian function for fitting residuals
    fGaussFunc = funcF.createFunctionByName("Gaussian", "G");

    // Create fit residuals histogram
    fResidualsHist = hf.createHistogram1D ("Histogram 1D",50,-3,3);

    // Will need random number generator for generating tracks
    // and for smearing the measurement points
    fRan = new java.util.Random ();

    // Create an IPlotter
    fPlotter = fAnalFactory.createPlotterFactory ().create ();
    // Create a region for the points plot and for the histogram.
    fPlotter.createRegions(2,1);

    // Now embed the plotter into the application frame
    add (PlotterUtilities.componentForPlotter (fPlotter), BorderLayout.CENTER);

    // Use a textfield for an input parameter.
    fNumCurvesField =
      new JTextField (Integer.toString (fNumCurves), 10);

    // Use a textfield for an input parameter.
    fSmearField =
      new JTextField (Double.toString (fCurveSmear), 10);

    // If return hit after entering text, the
    // actionPerformed will be invoked.
    fNumCurvesField.addActionListener (this);
    fSmearField.addActionListener (this);

    fGoButton = new JButton ("Go");
    fGoButton.addActionListener (this);

    fClearButton = new JButton ("Clear");
    fClearButton.addActionListener (this);

    fExitButton = new JButton ("Exit");
    fExitButton.addActionListener (this);

    JPanel control_panel = new JPanel (new GridLayout (1,5));

    control_panel.add (fNumCurvesField);
    control_panel.add (fSmearField);
    control_panel.add (fGoButton);
    control_panel.add (fClearButton);
    control_panel.add (fExitButton);

    // Put control panel at bottom of panel.
    add (control_panel,"South");

    // Create dummy data to provide the x axis values
    // for the curves.
    double dx = 5.0;
    fX[0] = 0.0;
    for (int i=1; i < 20; i++){
        fX[i] = fX[i-1] + dx;
    }

  } // init


  /** Respond to controls. **/
  public void actionPerformed (ActionEvent e) {

    Object source = e.getSource ();
    if (source == fGoButton || source == fNumCurvesField
                           || source == fSmearField) {
      String strNumDataPoints = fNumCurvesField.getText ();
      String strCurveSmear = fSmearField.getText ();
      try{
          fNumCurves = Integer.parseInt (strNumDataPoints);
          fCurveSmear = Double.parseDouble (strCurveSmear);
      }
      catch (NumberFormatException ex) {
        // Could open an error dialog here but just
        // display a message on the browser status line.
        System.out.println ("Bad input value");
        return;
      }

      fGoButton.setEnabled (false);
      fClearButton.setEnabled (false);
      if (fThread != null) stop ();
      fThread = new Thread (this);
      fThread.start ();

    }
    else if ( source == fClearButton) {
        fResidualsHist.reset ();
        if (!fFirstPlot) fPlotter.clearRegions ();
        fFirstPlot = true;
    } else
        System.exit (0);

  } // actionPerformed

  public void stop (){
    // If thread is still running, setting this
    // flag will kill it.
    fThread = null;
  } // stop

  /**
    *  Generate the tracks in a thread and then
    *  fit the resulting points to a polynominal.
    *  Fill a histogram with the residuals and fit
    *  a Gaussian to it. This histogram will continue
    *  to fill until the Clear button is pushed.
   **/
  public void run () {

    for (int i=0; i < fNumCurves; i++){
      // Stop the thread if flag set
      if (fThread == null) return;

      // Generate a random track.
      double [] gen_params =
         genRanCurve (fXMax-fXMin,
                      fYMax-fYMin,
                      fX, fY, fYErr, fCurveSmear);

      // Clear the data point set
      fDps2D.clear ();

      // Fill the data point set with the generated data values.
      for (int j = 0; j < fX.length; j++ ) {
        fDps2D.addPoint ();
        fDps2D.point (j).coordinate (0).setValue (fX[j]);
        fDps2D.point (j).coordinate (1).setValue (fY[j]);
        fDps2D.point (j).coordinate (1).setErrorPlus (fYErr[j]);
      }

      // The data points are in a 2D data set. Use IFitData to tell
      // the fitter which coordinate (0) to treat as x and which to
      // treat as y (1) in a fit of y=f(x)
      fFitData.create1DConnection (fDps2D,0,1);
      boolean fit_p_ok = true;
      boolean fit_g_ok = true;

      // Fit a polynominal to the data points. In certain
      // pathological cases, the fit can fail to where an
      // exception is thrown. So we catch such exceptions
      // rather than letting the program stop.
      try {
        fFittedP = fFitter.fit (fFitData,fP2Func);
      }
      catch (Exception e) {
        System.out.println ("Fit exception: " + e);
        fit_p_ok = false;
      }

      // Residuals == difference between the measured value
      // and the fitted value at the points at each x position
      if (fit_p_ok) {
        // Get the parameters of the polymonial fit to the data.
        double [] fit_params = fFittedP.fittedParameters ();

        // Calculate the residual and fill the histogram
        for (int j=0; j < fX.length; j++) {
          double y_fit = fit_params[0] + fit_params[1]*fX[j]
                                  + fit_params[2]*fX[j]*fX[j];
          fResidualsHist.fill (fY[j] - y_fit);
        }
        // Now fit a Gaussian to the residuals distribution.
        // Catch exceptions if the fit fails.
        try {
          fFittedG = fFitter.fit(fResidualsHist,fGaussFunc);
        }
        catch (Exception e) {
          fit_g_ok = false;
          System.out.println ("Gaussian fit exception: " + e);
        }
      }

      // Clear the previous plot except for the very first time.
      if (!fFirstPlot) fPlotter.clearRegions ();
      fFirstPlot = false;

      // Plot and show the data with the fitted functions.
      fPlotter.region (0).plot (fDps2D);
      if (fit_p_ok) fPlotter.region(0).plot (fFittedP.fittedFunction () );
      fPlotter.region (1).plot (fResidualsHist);
      if (fit_g_ok) fPlotter.region(1).plot (fFittedG.fittedFunction () );
      fPlotter.show ();

      // Pause briefly to let users see the track.
      try {
          Thread.sleep (200);
      } catch (InterruptedException e) {}
    }

    fGoButton.setEnabled (true);
    fClearButton.setEnabled (true);

  } // run

  /**
    *  Generate a quadratic plot and obtain points along the curve.
    *  Smear the vertical coordinate with a Gaussian.
   **/
  double [] genRanCurve (double x_range, double y_range,
                         double [] x_curve, double [] y_curve,
                         double [] y_curve_err,
                         double smear){

    // Parameters for a quadratic line.
    double [] quadParam = new double[3];

    // Simulated quadratic
    double y0 = y_range* (0.5 + 0.25 * fRan.nextDouble ());
    double y1 = y_range * fRan.nextDouble ();

    // Choose some dummy paramters for the polynominal
    quadParam[0] = y0;
    quadParam[1] = (y1-y0)/ (8.0*x_range);
    quadParam[2] = (fRan.nextDouble () - 0.5)/100.0;

    // Make the points and errors along a quadratic line
    for (int i=0; i < x_curve.length; i++) {
      y_curve[i] = y0 + quadParam[1]*x_curve[i]
                     + quadParam[2]*x_curve[i]*x_curve[i];

      double curve_err = smear*fRan.nextGaussian ();

      // Add smear factor for this point
      y_curve[i] += curve_err;

      // Create a dummy average std.dev. error on the y value
      // for this x position.
      y_curve_err[i] =  (1.0 + fRan.nextDouble () ) * smear;

    }

    // Return the track parameters.
    return quadParam;

  } // genRanCurve

   /**
    * @param args the command line arguments
    */
   public static void main (String[] args) {
      int frame_width  = 450;
      int frame_height = 450;

      JFrame frame = new JFrame ("Fitting Data with JAIDA");
      frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);

      frame.getContentPane ().add (new PolyFitJAIDAApp ());
      frame.setSize (new Dimension (frame_width,frame_height));
      frame.setVisible (true);

   } // main

} // PolyFitJAIDAApp

 

See the next page for a discussion of the JAIDA code in the above program.

 

Most recent update: Dec.15, 2005

 

              Tech
Tech APIs
Exercises

           Physics
Math/Science APIs
JAIDA Program
  Demo 1
Demo 1 Discussion
Exercises

  Part I Part II Part III
Java Core 1  2  3  4  5  6  7  8  9  10  11  12 13 14 15 16 17
18 19 20
21
22 23 24
Supplements

1  2  3  4  5  6  7  8  9  10  11  12

Tech 1  2  3  4  5  6  7  8  9  10  11  12
Physics 1  2  3  4  5  6  7  8  9  10  11  12

Java is a trademark of Sun Microsystems, Inc.