Difference between revisions of "Mattione GlueX Analysis Factories"

From GlueXWiki
Jump to: navigation, search
(Details for Developers and for writing Custom DAnalysisActions)
(Details for Developers and for writing Custom DAnalysisActions)
Line 100: Line 100:
 
** The <span style="color:#008000">Perform_Action</span>() function is then called on each of the input <span style="color:#0000FF">DParticleCombo</span> (see "Perform Action").  
 
** The <span style="color:#008000">Perform_Action</span>() function is then called on each of the input <span style="color:#0000FF">DParticleCombo</span> (see "Perform Action").  
  
* '''Initialization''': The Initialize() function is called by the function-call operator and is used to create histograms, <span style="color:#0000FF">TTree</span> objects, setup cuts, or any other operations that are needed to prepare the object for performing the desired action.  
+
* '''Initialization''': The <span style="color:#008000">Initialize</span>() function is called by the function-call operator and is used to create histograms, <span style="color:#0000FF">TTree</span> objects, setup cuts, or any other operations that are needed to prepare the object for performing the desired action.  
 
** This function must be defined in each class deriving from <span style="color:#0000FF">DAnalysisAction</span>, and should be declared as private.  
 
** This function must be defined in each class deriving from <span style="color:#0000FF">DAnalysisAction</span>, and should be declared as private.  
  
 
* '''Creating ROOT Objects''': In the Initialize() function, the following rules should be followed to create directories, histograms, etc. in the output ROOT file in an organized, thread-safe manner:
 
* '''Creating ROOT Objects''': In the Initialize() function, the following rules should be followed to create directories, histograms, etc. in the output ROOT file in an organized, thread-safe manner:
** Acquire and release a JANA ROOT lock before (<span style="color:#0000FF">DAnalysisAction</span>::Get_Application()->RootWriteLock()) and after (<span style="color:#0000FF">DAnalysisAction</span>::Get_Application()->RootUnLock()) reading/creating/changing directories and creating histograms.  
+
** Acquire and release a JANA ROOT lock before (<span style="color:#0000FF">DAnalysisAction</span>::<span style="color:#008000">Get_Application</span>()-><span style="color:#008000">RootWriteLock</span>()) and after (<span style="color:#0000FF">DAnalysisAction</span>::<span style="color:#008000">Get_Application</span>()-><span style="color:#008000">RootUnLock</span>()) reading/creating/changing directories and creating histograms.  
 
** Special care should be taken when creating directories in the output file, because they may have already been created by another thread.  Follow these rules for creating directories:
 
** Special care should be taken when creating directories in the output file, because they may have already been created by another thread.  Follow these rules for creating directories:
*** Call <span style="color:#0000FF">DAnalysisAction</span>::CreateAndChangeTo_ActionDirectory() to create and change-to a directory that is unique to this analysis action (but NOT to this <span style="color:#0000FF">DAnalysisAction</span> (important multi-threading distinction!: each thread will have it's own unique instance of the <span style="color:#0000FF">DAnalysisAction</span> object)), yet is shared between threads.  If the directory already exists (created by another thread), it just changes to it.  This directory (and any desired sub-directories) should be used to store any histograms, trees, etc. that are created by this analysis action.  This is why each <span style="color:#0000FF">DReaction</span> needs to have a unique name, and each <span style="color:#0000FF">DAnalysisAction</span> within a given <span style="color:#0000FF">DReaction</span> needs to have a unique name.   
+
*** Call <span style="color:#0000FF">DAnalysisAction</span>::<span style="color:#008000">CreateAndChangeTo_ActionDirectory</span>() to create and change-to a directory that is unique to this analysis action (but NOT to this <span style="color:#0000FF">DAnalysisAction</span> (important multi-threading distinction!: each thread will have it's own unique instance of the <span style="color:#0000FF">DAnalysisAction</span> object)), yet is shared between threads.  If the directory already exists (created by another thread), it just changes to it.  This directory (and any desired sub-directories) should be used to store any histograms, trees, etc. that are created by this analysis action.  This is why each <span style="color:#0000FF">DReaction</span> needs to have a unique name, and each <span style="color:#0000FF">DAnalysisAction</span> within a given <span style="color:#0000FF">DReaction</span> needs to have a unique name.   
*** Call <span style="color:#0000FF">DAnalysisAction</span>::CreateAndChangeTo_Directory() to create and change-to new sub-directories within the main action directory.  If the directory already exists (created by another thread), it just changes to it.   
+
*** Call <span style="color:#0000FF">DAnalysisAction</span>::<span style="color:#008000">CreateAndChangeTo_Directory</span>() to create and change-to new sub-directories within the main action directory.  If the directory already exists (created by another thread), it just changes to it.   
** Before creating a histogram, check the directory to make sure that it hasn't already been created by another thread (if so, just grab the pre-existing pointer from the directory rather than creating a duplicate histogram).  
+
** Before creating a histogram, check the directory to make sure that it hasn't already been created by another thread (if so, just grab the preexisting pointer from the directory rather than creating a duplicate histogram).  
  
* '''Perform Action''': The Perform_Action() function is called by the function-call operator and is used to execute the action on a given <span style="color:#0000FF">DParticleCombo</span>.  It returns true/false if the <span style="color:#0000FF">DParticleCombo</span> passes/fails the action.  
+
* '''Perform Action''': The <span style="color:#008000">Perform_Action()</span> function is called by the function-call operator and is used to execute the action on a given <span style="color:#0000FF">DParticleCombo</span>.  It returns true/false if the <span style="color:#0000FF">DParticleCombo</span> passes/fails the action.  
 
** In addition to the <span style="color:#0000FF">JEventLoop</span> and the <span style="color:#0000FF">DParticleCombo</span> to be acted-upon, this function is passed in a deque of pair<const <span style="color:#0000FF">DParticleCombo</span>*, bool>: these contain the <span style="color:#0000FF">DParticleCombo</span> objects that the action has ALREADY acted upon, and boolean flags that indicate whether they passed or failed the action.  
 
** In addition to the <span style="color:#0000FF">JEventLoop</span> and the <span style="color:#0000FF">DParticleCombo</span> to be acted-upon, this function is passed in a deque of pair<const <span style="color:#0000FF">DParticleCombo</span>*, bool>: these contain the <span style="color:#0000FF">DParticleCombo</span> objects that the action has ALREADY acted upon, and boolean flags that indicate whether they passed or failed the action.  
 
*** The previous combos can be used (for example) to determine whether or not the quantities to be histogrammed are identical to those from a previous <span style="color:#0000FF">DParticleCombo</span> and should be skipped to prevent double-counting (e.g. when histogramming the momentum of K<sup>+</sup> candidates, two different <span style="color:#0000FF">DParticleCombo</span> objects can have the same track used as the K<sup>+</sup> candidate (but be unique otherwise)).  
 
*** The previous combos can be used (for example) to determine whether or not the quantities to be histogrammed are identical to those from a previous <span style="color:#0000FF">DParticleCombo</span> and should be skipped to prevent double-counting (e.g. when histogramming the momentum of K<sup>+</sup> candidates, two different <span style="color:#0000FF">DParticleCombo</span> objects can have the same track used as the K<sup>+</sup> candidate (but be unique otherwise)).  
 
** This function must be defined in each class deriving from <span style="color:#0000FF">DAnalysisAction</span>, and should be declared as private.
 
** This function must be defined in each class deriving from <span style="color:#0000FF">DAnalysisAction</span>, and should be declared as private.
  
* '''Filling ROOT Objects''': In the Perform_Action() function, a JANA ROOT lock should be acquired before (<span style="color:#0000FF">DAnalysisAction</span>::Get_Application()->RootWriteLock()) and released after (<span style="color:#0000FF">DAnalysisAction</span>::Get_Application()->RootUnLock()) filling any ROOT TTrees or histograms.  
+
* '''Filling ROOT Objects''': In the <span style="color:#008000">Perform_Action</span>() function, a JANA ROOT lock should be acquired before (<span style="color:#0000FF">DAnalysisAction</span>::<span style="color:#008000">Get_Application</span>()-><span style="color:#008000">RootWriteLock</span>()) and released after (<span style="color:#0000FF">DAnalysisAction</span>::<span style="color:#008000">Get_Application</span>()-><span style="color:#008000">RootUnLock</span>()) filling any ROOT <span style="color:#0000FF">TTree</span> objects or histograms.  
 
** To reduce bottlenecks, the lock should be held for the minimum amount of time possible: perform all calculations prior to acquiring the lock, and release it immediately after filling everything.   
 
** To reduce bottlenecks, the lock should be held for the minimum amount of time possible: perform all calculations prior to acquiring the lock, and release it immediately after filling everything.   
 
** To reduce overhead, try to acquire the lock only once per action if possible.
 
** To reduce overhead, try to acquire the lock only once per action if possible.

Revision as of 21:56, 12 October 2012

Summary

  • In his/her plugin, a user specifies the reaction(s) he/she wants to study (DReaction), along with the histograms and cuts to perform (DAnalysisAction), then just asks JANA for the results (DAnalysisResults).
  • Through this process, the program creates all possible track combinations for the desired reaction (DParticleCombo, tag "PreKinFit"), kinematic fits the event to the reaction (DKinFitResults), and updates the tracks with the new kinfit information (DParticleCombo, no tag).
  • The DAnalysisAction objects encapsulate the setup and execution of an action (e.g. cut, histogram) into a single object.
    • Many common actions are predefined in DANA (see libraries/ANALYSIS/DHistogramActions.h and libraries/ANALYSIS/DCutActions.h), but the user can write custom ones for their analysis in their plugin.
  • The DAnalysisResults objects indicate which DParticleCombo objects have passed/failed the DAnalysisAction cuts for each DReaction.

Quick Start

  • NOTE: For an example of the below steps, see one of the existing analysis plugins (e.g. sim-recon/src/programs/Analysis/plugins/b1pi_hists).
  • Create a new plugin containing a DEventProcessor, DReaction_factory, and DFactoryGenerator (for the DReaction_factory).
  • Setup the DEventProcessor and DFactoryGenerator.
    • In DEventProcessor, make sure to grab the DAnalysisResults objects from the JEventLoop in the evnt() method, and to add the DFactoryGenerator to the application in the InitPlugin() method.
    • The DFactoryGenerator contents can be identical to those from sim-recon/src/programs/Analysis/plugins/b1pi_hists, although you may want a unique class name in case more than one plugin is used at once.
  • Create desired custom DAnalysisAction objects for your analysis (if any).
    • See existing analysis actions in sim-recon/src/libraries/ANALYSIS/DHistogramActions.h and sim-recon/src/libraries/ANALYSIS/DCutActions.h for examples.
  • In DReaction_factory::init(void), create a DReaction object (with a unique name!) for each analysis you want to perform, specify the kinematic fit type for it, specify the DAnalysisActions for each, and add them to _data.
    • Make sure to call SetFactoryFlag(PERSISTANT); in this function.
    • The DAnalysisAction objects are executed sequentially for each possible particle combination (DParticleCombo) matching the given DReaction, until it fails a cut.
    • If more than one DAnalysisAction object of a given type is used in a DReaction, all of their constructors must be called with a unique identifier string (including the default "").
    • More details on analysis actions in sim-recon/src/libraries/ANALYSIS/: DAnalysisAction.*, DHistogramActions.*, DCutActions.*
  • Compile and run hd_root with your plugin (preferably on REST data).

Analysis Procedure Details

Overview

  • Generate every possible combination of particles (DParticleCombo) that can represent the desired DReaction objects.
    • This can yield many DParticleCombo objects per event.
    • This is regardless of PID, # of remaining tracks, timing, track position or momentum, etc.
      • Only extremely loose cuts are placed by default on PID, track-vertex-z, and beam photon time prior to creating DParticleCombo objects: this is to avoid memory spikes from events with too many particles.
        • These can be changed by modifying the command line parameters defined in DParticleComboBlueprint_factory::brun() and DParticleCombo_factory_PreKinFit::brun().
  • User-specified cuts are then used to eliminate DParticleCombo objects that do not much the desired DReaction objects (e.g. PID, kinematic fit, invariant mass, etc.)
    • This is done by executing DAnalysisActions (e.g. histogramming, cutting) in sequence on each DParticleCombo until it fails a cut.
    • The kinematic fit (if specified) is not performed until the kinematic fit results are required for a DAnalysisAction (DAnalysisAction::Get_UseKinFitResultsFlag() == true). This saves time and memory by letting users cut DParticleCombo objects prior to fitting.
  • Save the results to disk further analysis (currently not built-in (except for histograms)).

REST Generated Tracks and Reconstructed Detector Data

  • NOTE: These definitions are spread throughout sim-recon/src/libraries/
  • DMCThrown: The generated particles for the event.
  • DBeamPhoton: The generated beam photon for the event.
  • DTrackTimeBased: The particles reconstructed from time-based tracking. One DTrackTimeBased object is created per PID hypothesis (defaults are π+, K+, and p for positively charged tracks, and π-, K- for negatively charged tracks).
  • DBCALShower: The reconstructed showers in the barrel calorimeter.
  • DFCALShower: The reconstructed showers in the forward calorimeter.
  • DTOFPoint: The reconstructed hit in the time-of-flight scintillators.
  • DSCHit: The raw detected SC hit.
  • DMCReaction, DTaggerHit: These are currently unused by the analysis framework.

Reconstructed Particles

  • NOTE: These are located in sim-recon/src/libraries/PID/
  • DBeamPhoton_factory: Currently just reads the generated DBeamPhoton objects in from the hddm file.
  • DChargedTrackHypothesis_factory: Creates a DChargedTrackHypothesis object for each DTrackTimeBased object (one per PID hypothesis, can be more than one per particle). When creating the DChargedTrackHypothesis objects, the tracks are matched to hits in the SC, BCAL, FCAL, and TOF, and the PID FOM is calculated (from TOF and DC dE/dx).
  • DChargedTrack_factory: Creates a DChargedTrack object for each reconstructed charged particle. These contain all of the DChargedTrackHypothesis objects associated with this particle.
  • DNeutralShower_factory: Creates a DNeutralShower object for each DBCALShower and DFCALShower object that is not matched to any of the DChargedTrackHypothesis objects.
  • DNeutralParticleHypothesis_factory: Creates two DNeutralParticleHypothesis objects for each DNeutralShower: a photon and neutron. The particle vertex is assumed to be the center of the target.

Reaction Analysis

  • NOTE: These are located in sim-recon/src/libraries/ANALYSIS/
  • DReaction_factory: Creates DReaction objects to be analyzed. Needs to be created in each analysis plugin.
  • DParticleComboBlueprint_factory: Creates a DParticleComboBlueprint object for each possible track combination, excluding any possible multiplicity from multiple beam photons. These DParticleComboBlueprint objects store pointers to which DChargedTrack and DNeutralShower objects will be used for each particle in the reaction. The beam photon multiplicity is handled in DParticleCombo_factory_PreKinFit.
  • DTrackTimeBased_factory_Reaction: If none of the existing DTrackTimeBased objects for a given reconstructed track have the needed PID for a particle in a DReaction, this creates new DTrackTimeBased objects for them (e.g. e-, or if one of the default PIDs (e.g. π+) fails reconstruction while the others don't (e.g. K+)). This uses the reconstructed momentum from the DTrackTimeBased object with the closest mass, and re-swims the track.
  • DChargedTrackHypothesis_factory_Reaction: Creates DChargedTrackHypothesis objects for the DTrackTimeBased objects created by DTrackTimeBased_factory_Reaction.
  • DParticleCombo_factory_PreKinFit: Creates a DParticleCombo object for each DParticleComboBlueprint, setting the data with the measured DChargedTrackHypothesis, DNeutralParticleHypothesis, and DBeamPhoton objects. A loose timing cut is placed between the RF time and the DBeamPhoton objects, and additional DParticleCombo objects are created if more than one DBeamPhoton object survives the cut.
  • DAnalysisResults_PreKinFit: Loops over the DAnalysisAction objects stored in each DReaction, executing them on the corresponding DParticleCombo objects until the kinematic fit results are needed for an action (DAnalysisAction::Get_UseKinFitResultsFlag() == true). This allows DAnalysisAction objects to reject background combinations prior to kinematic fitting to save time and memory. This creates and fills histograms of the number of events and DParticleCombo objects that survive each cut. This also creates and fills several reaction-independent histograms, such as thrown and reconstructed particle kinematics, and track multiplicity.
  • DKinFitResults_factory: For each DParticleCombo that survived all of the cuts executed in DAnalysisResults_PreKinFit, perform the kinematic fit specified by the DReaction objects (if desired).
  • DChargedTrackHypothesis_factory_KinFit: Create DChargedTrackHypothesis objects containing the kinematically fit track parameters. New, unique objects are created for each charged particle in each kinematically fit DParticleCombo.
  • DNeutralParticleHypothesis_factory_KinFit: Create DNeutralParticleHypothesis objects containing the kinematically fit track parameters. New, unique objects are created for each neutral particle in each kinematically fit DParticleCombo.
  • DBeamPhoton_factory_KinFit: Create DBeamPhoton objects containing the kinematically fit track parameters. New, unique objects are created for each beam photon in each kinematically fit DParticleCombo.
  • DParticleCombo_factory: Creates a new DParticleCombo object in each DParticleCombo that survived all of the cuts executed in DAnalysisResults_PreKinFit, but containing the kinematically fit track parameters (in addition to the original, measured parameters).
  • DAnalysisResults_factory: Loops over the DAnalysisAction objects stored in each DReaction, skipping those that were executed by DAnalysisResults_PreKinFit, and executing the remaining ones on the corresponding DParticleCombo objects created by DParticleCombo_factory. This creates and fills histograms of the number of events and DParticleCombo objects that survive each cut.

Analysis Utilities

  • DParticleID: Collection of functions used to match charged tracks to hits in the BCAL, FCAL, SC, and TOF, and to calculate the PID FOM from the TOF and DC dE/dx information.
  • DMCThrownMatching: Matches reconstructed particles to generated particles.
  • DAnalysisUtilities: Collection of utility functions used to facilitate physics analyses. These include methods for calculating invariant mass, missing mass, DOCA, etc., as well as methods for determining whether a DParticleCombo is in some ways similar to other DParticleCombo objects (e.g. the same track hypotheses make up a given decay chain) (useful for preventing double-counting when histogramming (e.g. invariant mass)).
  • DKinFitter_GlueX: See GlueX Kinematic Fitting

DAnalysisAction Objects

Overview

  • DAnalysisAction is a virtual base class that encapsulates the setup (e.g. histogram creation) and execution (cut, hist, etc.) of the contained action for each DParticleCombo that has passed all previous cuts.
  • These objects are typically used to cut DParticleCombo objects, histogram data, or save results in a TTree, although custom actions can be written to do just about anything (perform additional kinematic fits, etc.)
    • Many common actions (e.g. histogramming kinfit pulls, comparing thrown and reconstructed MC data, etc.) are pre-defined in src/libraries/ANALYSIS/ (see DHistogramActions.h and DCutActions.h)
    • Custom, analysis-specific actions can be created and stored in the user's plugin.
  • In the DAnalysisResults factories, the DAnalysisAction objects stored in each DReaction are sequentially executed on each of the corresponding DParticleCombo objects (until they fail one of the cuts).
    • The DAnalysisAction objects should be added to the DReaction objects in the order that they will be executed.

Details for Developers and for writing Custom DAnalysisActions

  • Construction: Each class deriving from DAnalysisAction should be designed so that no user input is required after calling the constructor.
    • For example, the constructor should contain the DReaction that the action is to be performed on, the values of the cuts to be performed, whether to operate on pre-kinematic-fit or post-kinematic-fit data, etc.
      • However, the user should be allowed to perform "optional" modifications outside of the constructor (e.g. changing the default histogram range and binning).
    • The default DAnalysisAction constructor has been disabled (made private), so each constructor of each class deriving from DAnalysisAction must explicitly call the DAnalysisAction public constructor. This is to enforce proper setup of each DAnalysisAction.
    • Within a DReaction, each DAnalysisAction needs to have a unique name (for safe multi-threaded creation of histograms). This name is composed of a base-name built into the class itself that is unique to that class, and an optional unique identifier string that should be passed into the constructor by the user if more than one instance of a given class is performed in a given DReaction (e.g. make two different invariant mass histograms).
  • Execution: Each DAnalysisAction is executed by calling the function-call operator (operator()).
    • In addition to the JEventLoop, this operator needs to be passed a deque of pair<const DParticleCombo*, bool>: these contain the DParticleCombo objects that the DAnalysisAction should be executed on, and boolean flags that afterwards will indicate if the given DParticleCombo objects passed or failed the cuts performed by that action (if any).
    • If the DAnalysisAction has not yet been initialized, the function-call operator will first call the Initialize() function (see "Initialization").
    • The Perform_Action() function is then called on each of the input DParticleCombo (see "Perform Action").
  • Initialization: The Initialize() function is called by the function-call operator and is used to create histograms, TTree objects, setup cuts, or any other operations that are needed to prepare the object for performing the desired action.
    • This function must be defined in each class deriving from DAnalysisAction, and should be declared as private.
  • Creating ROOT Objects: In the Initialize() function, the following rules should be followed to create directories, histograms, etc. in the output ROOT file in an organized, thread-safe manner:
    • Acquire and release a JANA ROOT lock before (DAnalysisAction::Get_Application()->RootWriteLock()) and after (DAnalysisAction::Get_Application()->RootUnLock()) reading/creating/changing directories and creating histograms.
    • Special care should be taken when creating directories in the output file, because they may have already been created by another thread. Follow these rules for creating directories:
      • Call DAnalysisAction::CreateAndChangeTo_ActionDirectory() to create and change-to a directory that is unique to this analysis action (but NOT to this DAnalysisAction (important multi-threading distinction!: each thread will have it's own unique instance of the DAnalysisAction object)), yet is shared between threads. If the directory already exists (created by another thread), it just changes to it. This directory (and any desired sub-directories) should be used to store any histograms, trees, etc. that are created by this analysis action. This is why each DReaction needs to have a unique name, and each DAnalysisAction within a given DReaction needs to have a unique name.
      • Call DAnalysisAction::CreateAndChangeTo_Directory() to create and change-to new sub-directories within the main action directory. If the directory already exists (created by another thread), it just changes to it.
    • Before creating a histogram, check the directory to make sure that it hasn't already been created by another thread (if so, just grab the preexisting pointer from the directory rather than creating a duplicate histogram).
  • Perform Action: The Perform_Action() function is called by the function-call operator and is used to execute the action on a given DParticleCombo. It returns true/false if the DParticleCombo passes/fails the action.
    • In addition to the JEventLoop and the DParticleCombo to be acted-upon, this function is passed in a deque of pair<const DParticleCombo*, bool>: these contain the DParticleCombo objects that the action has ALREADY acted upon, and boolean flags that indicate whether they passed or failed the action.
      • The previous combos can be used (for example) to determine whether or not the quantities to be histogrammed are identical to those from a previous DParticleCombo and should be skipped to prevent double-counting (e.g. when histogramming the momentum of K+ candidates, two different DParticleCombo objects can have the same track used as the K+ candidate (but be unique otherwise)).
    • This function must be defined in each class deriving from DAnalysisAction, and should be declared as private.
  • Filling ROOT Objects: In the Perform_Action() function, a JANA ROOT lock should be acquired before (DAnalysisAction::Get_Application()->RootWriteLock()) and released after (DAnalysisAction::Get_Application()->RootUnLock()) filling any ROOT TTree objects or histograms.
    • To reduce bottlenecks, the lock should be held for the minimum amount of time possible: perform all calculations prior to acquiring the lock, and release it immediately after filling everything.
    • To reduce overhead, try to acquire the lock only once per action if possible.

DReaction

  • Located in src/libraries/ANALYSIS/
  • The user should create a DReaction object in their plugin for each analysis they want to perform (can analyze more than one at once).
  • Particle_t is defined in src/libraries/include/particleType.h
  • For an example on how to setup a DReaction, see the "User Plugin DReaction_factory" section below.
  • IMPORTANT NOTE FOR ANALYZERS: If a PID you need to use for your analysis is not defined by Particle_t, you can use "Unknown" instead (e.g. a rare resonance) (if you don't need its mass or charge). However, only one PID per DReaction (besides the target) can have the Particle_t of "Unknown" (e.g. the resonance you are studying), because the framework will not distinguish between the two. If you need more than one PID that is not included in Particle_t, then add all of the PIDs you need but one to particleType.h (along with their charges, masses, names, etc.) and check in the update (because other people may need those particles too!).
  • IMPORTANT NOTE FOR DEVELOPERS: Grabbing the DReaction objects from JANA is tricky, because a user may have several factories per plugin, or may be running several plugins at once. See DParticleComboBlueprint_factory::evnt() for an example on how to correctly grab all DReaction objects from JANA.

Source Code

  • C++ code of class members (methods aren't shown):
class DReactionStep
{
  private:
    // PID MEMBERS:
    Particle_t dInitialParticleID; //e.g. lambda, gamma
    Particle_t dTargetParticleID; //Unknown for no target
    deque<Particle_t> dFinalParticleIDs;
 
    // CONTROL MEMBERS:
    int dMissingParticleIndex; //-1 for no missing particles, else final state particle at this index is missing (0 -> x)
};
class DReaction : public JObject
{
  private:
    // REACTION AND ANALYSIS MEMBERS:
    deque<const DReactionStep*> dReactionSteps;
    deque<DAnalysisAction*> dAnalysisActions;
 
    // CONTROL MEMBERS:
    string dReactionName; //must be unique
    DKinFitType dKinFitType; //defined in ANALYSIS/DKinFitResults.h
    deque<size_t> dDecayingParticlesExcludedFromP4Kinfit; //to exclude decaying particles from the kinematic fit (resonances are automatically excluded) //size_t is step index where it is a parent
};

DParticleCombo

  • Located in src/libraries/ANALYSIS/

Particles

  • Particle data is contained in either DChargedTrackHypothesis, DNeutralParticleHypothesis, DBeamPhoton, or DKinematicData (for reconstructed missing or decaying particles) objects.
    • However, all of these objects are stored in DParticleComboStep as pointers to DKinematicData. If you want the DBeamPhoton, etc. objects, you need to cast them back that type.
  • The order of the particles and steps are the same as specified by the DReaction and DReactionStep objects.
  • Both the measured data and the kinematic fit data are accessible from these classes.
    • In DParticleCombo objects grabbed from the "KinFit" tag factory, the measured data is stored in the both the "default" and the "measured" members (e.g. dFinalParticles and dFinalParticles_Measured).
    • In DParticleCombo objects grabbed from the default (no tag) factory, the kinematic data is stored in the "default" members (e.g. dFinalParticles) and the measured data is stored in the "measured" members (e.g. dFinalParticles_Measured).
  • The source objects (DChargedTrack and DNeutralShower) and PIDs are accessible through the DParticleComboBlueprintStep.
  • Particles that are missing or are decaying may have NULL pointers stored for the objects.
    • The source and measured pointers will be NULL, and the "default" pointers will be NULL unless they are constrained by the kinematic fit (e.g. resonances are not constrained, but a Λ or a missing recoil proton will be).

Source Code

  • C++ code of class members (methods aren't shown):
class DParticleComboStep
{
  private:
    // BLUEPRINT:
    const DParticleComboBlueprintStep* dParticleComboBlueprintStep; //contains PIDs, source objects
 
    // INITIAL PARTICLES:
    const DKinematicData* dInitialParticle; //if is null: decaying or beam particle not yet set!
    const DKinematicData* dInitialParticle_Measured; //if is null: decaying or beam particle not yet set!
    const DKinematicData* dTargetParticle; //NULL for no target
 
    // FINAL PARTICLES:
    deque<const DKinematicData*> dFinalParticles; //if particle is null: missing or decaying! //these are DChargedTrackHypothesis or DNeutralParticleHypothesis objects if detected
    deque<const DKinematicData*> dFinalParticles_Measured; //if particle is null: missing or decaying! //these are DChargedTrackHypothesis or DNeutralParticleHypothesis objects if detected
};
class DParticleCombo : public JObject
{
  private:
    const DReaction* dReaction;
    const DKinFitResults* dKinFitResults;
    deque<const DParticleComboStep*> dParticleComboSteps;
};

User Plugin DReaction_factory

  • This is from sim-recon/src/programs/Analysis/plugins/b1pi_hists/
//------------------
// init
//------------------
jerror_t DReaction_factory::init(void)
{
  // Setting the PERSISTANT prevents JANA from deleting
  // the objects every event so we only create them once.
  SetFactoryFlag(PERSISTANT);
 
  DReaction* locReaction;
  DReactionStep* locReactionStep;
 
  // Make as many DReaction objects as desired
 
  deque<DReactionStep*> locReactionSteps;
 
/**************************************************** b1pi Steps ****************************************************/
 
  //g, p -> X(2000), (p)
  locReactionStep = new DReactionStep();
  locReactionStep->Set_InitialParticleID(Gamma);
  locReactionStep->Set_TargetParticleID(Proton);
  locReactionStep->Add_FinalParticleID(Unknown); //x(2000)
  locReactionStep->Add_FinalParticleID(Proton);
  locReactionStep->Set_MissingParticleIndex(1); //proton missing
  locReactionSteps.push_back(locReactionStep);
  dReactionStepPool.push_back(locReactionStep); //prevent memory leak
 
  //x(2000) -> b1(1235)+, pi-
  locReactionStep = new DReactionStep();
  locReactionStep->Set_InitialParticleID(Unknown); //x(2000)
  locReactionStep->Set_TargetParticleID(Unknown); //no target for this step
  locReactionStep->Add_FinalParticleID(b1_1235_Plus);
  locReactionStep->Add_FinalParticleID(PiMinus);
  locReactionStep->Set_MissingParticleIndex(-1); //none missing
  locReactionSteps.push_back(locReactionStep);
  dReactionStepPool.push_back(locReactionStep); //prevent memory leak
 
  //b1(1235)+ -> omega, pi-
  locReactionStep = new DReactionStep();
  locReactionStep->Set_InitialParticleID(b1_1235_Plus);
  locReactionStep->Set_TargetParticleID(Unknown); //no target for this step
  locReactionStep->Add_FinalParticleID(omega);
  locReactionStep->Add_FinalParticleID(PiPlus);
  locReactionStep->Set_MissingParticleIndex(-1); //none missing
  locReactionSteps.push_back(locReactionStep);
  dReactionStepPool.push_back(locReactionStep); //prevent memory leak
 
  //omega -> pi+, pi-, pi0
  locReactionStep = new DReactionStep();
  locReactionStep->Set_InitialParticleID(omega);
  locReactionStep->Set_TargetParticleID(Unknown); //no target for this step
  locReactionStep->Add_FinalParticleID(PiPlus);
  locReactionStep->Add_FinalParticleID(PiMinus);
  locReactionStep->Add_FinalParticleID(Pi0);
  locReactionStep->Set_MissingParticleIndex(-1); //none missing
  locReactionSteps.push_back(locReactionStep);
  dReactionStepPool.push_back(locReactionStep); //prevent memory leak
 
  //pi0 -> gamma, gamma
  locReactionStep = new DReactionStep();
  locReactionStep->Set_InitialParticleID(Pi0);
  locReactionStep->Set_TargetParticleID(Unknown); //no target for this step
  locReactionStep->Add_FinalParticleID(Gamma);
  locReactionStep->Add_FinalParticleID(Gamma);
  locReactionStep->Set_MissingParticleIndex(-1); //none missing
  locReactionSteps.push_back(locReactionStep);
  dReactionStepPool.push_back(locReactionStep); //prevent memory leak
 
/**************************************************** b1pi ****************************************************/
 
  locReaction = new DReaction("b1pi");
  locReaction->Set_KinFitType(d_P4AndVertexFit); //defined in DKinFitResults.h
  for(size_t loc_i = 0; loc_i < locReactionSteps.size(); ++loc_i)
    locReaction->Add_ReactionStep(locReactionSteps[loc_i]);
 
  //Extremely Loose Mass Cuts
  locReaction->Add_AnalysisAction(new DCutAction_MissingMass(locReaction, false, 0.1, 1.6, "Proton_Loose")); //false: measured data
  locReaction->Add_AnalysisAction(new DCutAction_InvariantMass(locReaction, Pi0, false, 0.0, 0.5, "Pi0_Loose")); //false: measured data
  locReaction->Add_AnalysisAction(new DCutAction_InvariantMass(locReaction, omega, false, 0.2, 1.4, "omega_Loose")); //false: measured data
  locReaction->Add_AnalysisAction(new DCutAction_InvariantMass(locReaction, b1_1235_Plus, false, 0.6, 1.8, "b1(1235)+_Loose")); //false: measured data
  locReaction->Add_AnalysisAction(new DCutAction_InvariantMass(locReaction, Unknown, false, 1.0, 3.0, "X(2000)_Loose")); //false: measured data
 
  //PID
  locReaction->Add_AnalysisAction(new DHistogramAction_PID(locReaction));
  locReaction->Add_AnalysisAction(new DCutAction_AllPIDFOM(locReaction, 0.01)); //1%
  locReaction->Add_AnalysisAction(new DHistogramAction_TruePID(locReaction, "PostPID"));
 
  //Initial Mass Distributions
  locReaction->Add_AnalysisAction(new DHistogramAction_MissingMass(locReaction, false, 650, 0.3, 1.6, "PostPID")); //false: measured data
  locReaction->Add_AnalysisAction(new DHistogramAction_InvariantMass(locReaction, Pi0, false, 500, 0.0, 0.5, "Pi0_PostPID")); //false: measured data
  locReaction->Add_AnalysisAction(new DHistogramAction_InvariantMass(locReaction, omega, false, 600, 0.2, 1.4, "omega_PostPID")); //false: measured data
  locReaction->Add_AnalysisAction(new DHistogramAction_InvariantMass(locReaction, b1_1235_Plus, false, 600, 0.6, 1.8, "b1(1235)+_PostPID")); //false: measured data
  locReaction->Add_AnalysisAction(new DHistogramAction_InvariantMass(locReaction, Unknown, false, 1000, 1.0, 3.0, "X(2000)_PostPID")); //false: measured data
 
  //Kinematic Fit Results and Confidence Level Cut
  locReaction->Add_AnalysisAction(new DHistogramAction_KinFitResults(locReaction, 0.05)); //5% confidence level cut on pull histograms only
  locReaction->Add_AnalysisAction(new DCutAction_KinFitFOM(locReaction, 0.01)); //1%
 
  //Constrained Mass Distributions
  locReaction->Add_AnalysisAction(new DHistogramAction_MissingMass(locReaction, false, 650, 0.3, 1.6, "PostKinFitConLev")); //false: measured data
  locReaction->Add_AnalysisAction(new DHistogramAction_InvariantMass(locReaction, Pi0, false, 500, 0.0, 0.5, "Pi0_PostKinFitConLev")); //false: measured data
 
  //omega cut
  locReaction->Add_AnalysisAction(new DHistogramAction_InvariantMass(locReaction, omega, false, 600, 0.2, 1.4, "omega_PostKinFitConLev_Measured")); //false: measured data
  locReaction->Add_AnalysisAction(new DHistogramAction_InvariantMass(locReaction, omega, true, 600, 0.2, 1.4, "omega_PostKinFitConLev_KinFit")); //true: kinfit data
  locReaction->Add_AnalysisAction(new DCutAction_InvariantMass(locReaction, omega, true, 0.6, 1.0, "omega_PostKinFit")); //true: kinfit data
 
  //b1(1235)+ cut
  locReaction->Add_AnalysisAction(new DHistogramAction_InvariantMass(locReaction, b1_1235_Plus, false, 600, 0.6, 1.8, "b1(1235)+_PostKinFitConLev_Measured")); //false: measured data
  locReaction->Add_AnalysisAction(new DHistogramAction_InvariantMass(locReaction, b1_1235_Plus, true, 600, 0.6, 1.8, "b1(1235)+_PostKinFitConLev_KinFit")); //true: kinfit data
  locReaction->Add_AnalysisAction(new DCutAction_InvariantMass(locReaction, b1_1235_Plus, true, 1.1, 1.5, "b1(1235)+_PostKinFit")); //true: kinfit data
 
  //Signal Mass Hist
  locReaction->Add_AnalysisAction(new DHistogramAction_InvariantMass(locReaction, Unknown, false, 1000, 1.0, 3.0, "X(2000)_PostKinFitConLev_Measured")); //false: measured data
  locReaction->Add_AnalysisAction(new DHistogramAction_InvariantMass(locReaction, Unknown, true, 1000, 1.0, 3.0, "X(2000)_PostKinFitConLev_KinFit")); //true: kinfit data
 
  //Final Track Kinematics & PID Check
  locReaction->Add_AnalysisAction(new DHistogramAction_TrackVertexComparison(locReaction, "Final"));
  locReaction->Add_AnalysisAction(new DHistogramAction_ParticleComboKinematics(locReaction, true, "Final")); //true: kinfit data
  locReaction->Add_AnalysisAction(new DHistogramAction_TruePID(locReaction, "Final"));
 
  _data.push_back(locReaction); //save the DReaction
 
  return NOERROR;
}

User Plugin DEventProcessor

  • The DEventProcessor itself can be extremely minimal in content.
  • This is from sim-recon/src/programs/Analysis/plugins/b1pi_hists/
// Routine used to create our DEventProcessor
extern "C"
{
  void InitPlugin(JApplication *app)
  {
    InitJANAPlugin(app);
    app->AddProcessor(new DEventProcessor_b1pi_hists());
    app->AddFactoryGenerator(new DFactoryGenerator_DReaction());
  }
} // "C"
 
//------------------
// evnt
//------------------
jerror_t DEventProcessor_b1pi_hists::evnt(JEventLoop *locEventLoop, int eventnumber)
{
  vector<const DAnalysisResults*> locAnalysisResultsVector;
  locEventLoop->Get(locAnalysisResultsVector);
  return NOERROR;
}

Program Output

  • Other than the built-in histograms, you are currently on your own for this. Output any data you want in either your own DAnalysisAction or in your DEventProcessor::evnt() method.

Tricks for saving time and memory

  • Change the binning and/or range of the built-in histograms after constructing the corresponding DAnalysisAction object.
  • Because there may be many DParticleCombo objects for an event, perform as many cuts as possible prior to kinematic fitting.
    • This includes extremely-loose cuts (well beyond the range at which your final cut will be) on all of the physical quantities of interest (e.g. mass peaks).
  • To branch an analysis, create a new (but identical) DReaction object (with the same DReactionStep pointers), then simply repeat or change any of the DAnalysisActions.
    • If the DReactionStep objects are re-used, the framework will realize that the DParticleComboBlueprintStep objects will be the same, and will simply copy them.
      • Note that the DParticleCombo objects will not be identical since they will contain a pointer to a different DReaction.
      • If the kinematic fit type is also the same, it will skip the kinematic fit of the new DParticleCombo objects entirely and just copy the previous results since they will be identical.