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
|