This page contains more advanced tips for implementing an analysis in the highland framework. If you haven't yet completed the tutorial, you should do that first.
The following sections are currently included:
Using an analysis from another
It is possible for an analysis to use another. In the past this was solved with inheritance (a AnalysisAlgorithm inherits from another), however we found useful to allow using several analyses simultaneously from another one, and this was complicated using inheritance. This is how is done:
bool myAnalysis::Initialize(){
  
  
  _someoneElsesAnalysis = new someoneElsesAnalysis(this);
  
  UseAnalysis(_someoneElsesAnalysis);
  
  if (!_someoneElsesAnalysis->Initialize()) return false;
}
The "UseAnalysis" functionality is recommended when the analysis is an extension of a more basic one(s). For example, we might be interested in filling the same micro-tree variables as the more basic analysis, plus some others. Or in using the same systematics, etc. The call to the UseAnalysis method ensures that the managers are the same for both analyses. However we should call our selves the methods of the used analysis. For example:
void myAnalysis::DefineMicroTrees(bool addBase){
  
   
   
  _someoneElsesAnalysis->DefineMicroTrees(addBase);
  ...
}
Using a selection from another
A selection can use steps (cuts or actions) from another one since those are external to the selection (are added to it). We just need to include the corresponding header file where those steps are decleared, and then add the steps normally to our selection.
#include "someoneElsesSelection.hxx"
void mySelection::DefineSteps(){
    AddStep(StepBase::kCut,    "cut 1 name",    new myCut1(),              true);  
    AddStep(StepBase::kAction, "action 1 name", new myAction1(),               );  
    AddStep(StepBase::kAction, "action 2 name", new someoneElsesAction3(),     );  
    AddStep(StepBase::kCut,    "cut 2 name",    new someoneElsesCut5(),        );  
  ...
    
    SetBranchAlias(0,"trunk");
}
One can also copy steps from an existing selection to annother. For example, the code below add several steps in one go:
#include "someoneElsesSelection.hxx"
void mySelection::DefineSteps(){
    
    
    AddStep(StepBase::kCut,    "cut 1 name",    new myCut1(),              true);  
    AddStep(StepBase::kAction, "action 1 name", new myAction1(),               );  
    
    someoneElsesSelection _someoneElsesSelection;
    
    _someoneElsesSelection.Initialize();
    
    CopySteps(_someoneElsesSelection,0,1,10,0);    
  ...
    
    SetBranchAlias(0,"trunk");
}
Defining your own fiducial volume
The detector volume definitions are defined in the DetDef namespace in DetectorDefinition.cxx (in the psyche...Utils package). These definitions are the minimum and maximum corners of the box describing each detector.
In this example, the FGD and P0D fiducial volumes are defined in the FVDef namespace in FiducialVolumeDefinition.cxx (also in psyche...Utils package). These definitions are the amount to shrink the volume of the detector. The fiducial volume cuts can be overridden, using code similar to the following. It is wise to set the new volumes in the Initialize method of your analysis class:
bool myAnalysis::Initialize() {
  FVDef::FVdefminFGD1.SetXYZ(52.17, 57.17, 20.925);
  FVDef::FVdefmaxFGD1.SetXYZ(52.17, 47.17, 0);
  FVDef::FVdefminFGD2.SetXYZ(52.17, 57.17, 7.55);
  FVDef::FVdefmaxFGD2.SetXYZ(52.17, 47.17, -2.95);
  FVDef::FVdefminP0D.SetXYZ(142.79, 17.39, 96.48);
  FVDef::FVdefmaxP0D.SetXYZ(62.45, 40.99, 61.247);
}
This will update the FV seen in all places in the code.
Adding your own categories for drawing stacked histograms
When drawing histograms using the DrawingTools, it is possible to create stacked histograms based on the value of a variable in the micro-tree. For example, the "particle" category creates a stack based on the PDG code of a true track.
The standard categories are defined in TrackCategoryTools::AddStandardCategories(), which is called when the ND::categ() singleton is first accessed. The standard categories are filled by calling the anaUtils::FillCategories() function in baseAnalysis, which takes an AnaTrack as input.
It is possible to extend the categories by adding your own. These new definitions are saved in the output file, so are automatically available when the DrawingTools are instantiated in your analysis macro. There are two stages to using the new category. First, you must define the category, such as
bool myAnalysis::Initialize(){
  
  
  
  std::string mycateg_types[] = {"type1" , "type2", "type3" , "type4"};    
  int mycateg_codes[]         = { 1      , 2      , 3       , 4      };    
  int mycateg_colors[]        = { 7      , 4      , 3       , 2      };    
  const int NTYPES = sizeof(mycateg_types)/sizeof(mycateg_types[0]);       
  
  anaUtils::_categ->
AddCategory(
"mycateg", NTYPES, mycateg_types, mycateg_codes, mycateg_colors);
}
After defining the new category, we must fill it. We do this by implementing the FillCategories() method:
void myAnalysis::FillCategories() {
  AnaParticle* track = <your selected track from the ToyBox>
   
  
  
  int code = <whatever code you want to set for track>;
  cat().SetCode("mycateg", code);
}
Using parameters files
Parameters files should be placed in the parameters directory in your analysis package (myAnalysis/vXrY/parameters), and be named like myAnalysis.parameters.dat.
The syntax for specifying parameters looks like 
< myAnalysis.Cuts.HiMom.MinMom = 100. >
< myAnalysis.Cuts.Quality.MinTPCNodes = 35 >
< myAnalysis.Cuts.PID.DSThreshold = 300. >
< myAnalysis.Cuts.PID.DSTrShMax = 0.2 >
< myAnalysis.Calib.CalibrationAlgorithmName = algo1 >
The first bit of the parameter name must match your analysis' package name. You can use any string you like for the rest of parameter name.
To access the values of the parameters, use the ND::params() singleton, which is found in Parameters.hxx in psycheCore:
#include "Parameters.hxx"
void myAnalysis::SomeFunction() {
  double min_mom    = ND::params().
GetParameterD(
"myAnalysis.Cuts.HiMom.MinMom");
   int num_tpc_nodes = ND::params().
GetParameterI(
"myAnalysis.Cuts.Quality.MinTPCNodes");
   std::string algo  = ND::params().
GetParameterS(
"myAnalysis.Calib.CalibrationAlgorithmName");
}
Notice that your parameters file can overrride parameters from a lower level analysis package. For example myAnalysis.parameters.dat could contain the line
Number of toy experiments
Enable/disable the configuration with all selected systematics 
what means that the all_syst configuration will be run and with 100 toy experiments, regardless of what is set in baseAnalysis.parametsrs.dat
Extend the data classes with custom information
As mentioned previously, the DataClasses only contain a subset of the information found in the original analysis files. This means that they may not contain all the information you want in order to perform your analysis. It is possible to extend the existing event model (the data classes) by standard inheritance, as it is done in DataClasses (in highlandEventModel) with respect to BaseDataClasses (in psycheEventModel). Imagine a AnaExtendedParticle class extending AnaParticle with an additional property:
public :
  virtual ~AnaExtendedParticle(){}
  virtual AnaExtendedParticle* 
Clone() {
     return new AnaExtendedParticle(*this);
  }
protected:
  AnaExtendedParticle(
const AnaExtendedParticle& part):
AnaParticle(part){}
public:
  Float_t myProperty;
};
To fill the additional info a new InputConverer, let's call it myConverter, inheriting from the existing one (let's call it baseConverter) should be implemented. This converter should have methods as:
virtual AnaParticleB* MakeParticle() { 
return new AnaExtendedParticle(); }
 where the FillParticleInfo method is implemented in the .cxx file, and looks like:
void myConverter::FillParticleInfo(..., 
AnaParticleB* part){
    
   baseConverter::FillParticleInfo(..., part);
   
   AnaExtendedParticle* extPart = static_cast<AnaExtendedParticle*>(part);
   
   part->MyProperty = < some property from info in the input file >; 
}
Cut branches
Selection cuts can be splitted in branches in order to allow different cut chains in a single selection. For example we may want to have two different event selections, sharing the first set of cuts but splitting at some point. To be more concrete let's consider a practical example: we select events with vertex in SubDet1 or SubDet2. For events in SubDet2 two independent samples are selected in Water layers or scintillator layers. So far we have fouw branches: SubDet1, SubDet2, SubDet2-Water, SubDet2-Scint. Then we split each of this branches in three subbranches for events with 0, 1 or >1 pions. In practice:
void myMultiBranchSelection::DefineSteps(){
    
    
    AddSplit(2);
    
    
    numuCCSubDet1Selection _numuCCSubDet1Selection;
    _numuCCSubDet1Selection.Initialize();
    CopySteps(_numuCCSubDet1Selection,0,1,10,0);
    
    AddStep(0, StepBase::kAction, 
"find_pions_SubDet1",   
new FindPionsAction());
    
    
    AddStep(1,StepBase::kCut, "> 0 tracks & no SubDet1 tracks", new TotalMultiplicitySubDet2Cut(), true);
    
    numuCCSubDet2Selection _numuCCSubDet2Selection;
    _numuCCSubDet2Selection.Initialize();
    CopySteps(_numuCCSubDet2Selection,0,2,10,1);
    
    AddStep(1, StepBase::kAction, "fill_summary_SubDet2", new FillSummaryAction_numuCCMultiPiSubDet2());
    AddStep(1, StepBase::kAction, 
"find_pions_SubDet2",   
new FindPionsAction());
    
    AddSplit(2,1);
    AddStep(1,0, StepBase::kCut, 
"numu CC in water SubDet2", 
new WaterCut());
    AddStep(1,1, StepBase::kCut, 
"numu CC in scint SubDet2", 
new ScintillatorCut());
    
    
    
    AddSplit(3,0);
    AddStep(0,0, StepBase::kCut, 
"CC-0pi",   
new NoPionCut());
    AddStep(0,1, StepBase::kCut, 
"CC-1pi",   
new OnePionCut());
    AddStep(0,2, StepBase::kCut, 
"CC-Other", 
new OthersCut());
    
    
    AddSplit(3,1,0);
    AddStep(1,0,0, StepBase::kCut, 
"CC-0pi",   
new NoPionCut());
    AddStep(1,0,1, StepBase::kCut, 
"CC-1pi",   
new OnePionCut());
    AddStep(1,0,2, StepBase::kCut, 
"CC-Other", 
new OthersCut());
    
    AddSplit(3,1,1);
    AddStep(1,1,0, StepBase::kCut, 
"CC-0pi",   
new NoPionCut());
    AddStep(1,1,1, StepBase::kCut, 
"CC-1pi",   
new OnePionCut());
    AddStep(1,1,2, StepBase::kCut, 
"CC-Other", 
new OthersCut());
    
    
    
    
    SetBranchAlias(0, "numuCC_SubDet1",      0);
    SetBranchAlias(1, "numuCC_SubDet2",      1);
    SetBranchAlias(2, "numuCC_SubDet2-water",1,0);
    SetBranchAlias(3, "numuCC_SubDet2-scint",1,1);
    SetBranchAlias(4, "numuCC_SubDet1_CC-0pi",  0,0);
    SetBranchAlias(5, "numuCC_SubDet1_CC-1pi",  0,1);
    SetBranchAlias(6, "numuCC_SubDet1_CC-Other",0,2);
    SetBranchAlias(7, "numuCC_SubDet2-water_CC-0pi",  1,0,0);
    SetBranchAlias(8, "numuCC_SubDet2-water_CC-1pi",  1,0,1);
    SetBranchAlias(9, "numuCC_SubDet2-water_CC-Other",1,0,2);
    SetBranchAlias(10,"numuCC_SubDet2-scint_CC-0pi",  1,1,0);
    SetBranchAlias(11,"numuCC_SubDet2-scint_CC-1pi",  1,1,1);
    SetBranchAlias(12,"numuCC_SubDet2-scint_CC-Other",1,1,2);
}