bool
convertEvtToTree(const string&  evtFileName              = "testEvents.evt",
                 const string&  outFileName              = "testEvents.root",
                 const long int maxNmbEvents             = -1,
                 const string&  outTreeName              = "rootPwaEvtTree",
                 const string&  prodKinPartNamesObjName  = "prodKinParticles",
                 const string&  prodKinMomentaLeafName   = "prodKinMomenta",
                 const string&  decayKinPartNamesObjName = "decayKinParticles",
                 const string&  decayKinMomentaLeafName  = "decayKinMomenta",
                 const bool     debug                    = false)
{
	// open input file
	printInfo << "opening input file '" << evtFileName << "'" << endl;
	ifstream evtFile(evtFileName.c_str());
	if (not evtFile or not evtFile.good()) {
		printWarn << "cannot open input file '" << evtFileName << "'" << endl;
		return false;
	}

	// create output file
	printInfo << "creating output file '" << outFileName << "'" << endl;
	TFile* outFile = TFile::Open(outFileName.c_str(), "RECREATE");
	if (not outFile) {
		printErr << "cannot open output file '" << outFileName << "'" << endl;
		return false;
	}

	// create tree
	TTree* tree = new TTree(outTreeName.c_str(), outTreeName.c_str());
	if (not tree) {
		printErr << "problems creating tree '" << outTreeName << "' "
		         << "in file '" << outFileName << "'" << endl;
		return false;
	}

	// doit
	TClonesArray* prodKinPartNames  = new TClonesArray("TObjString");
	TClonesArray* decayKinPartNames = new TClonesArray("TObjString");
	const bool    success           = fillTreeFromEvt(evtFile, *tree,
	                                                  *prodKinPartNames, *decayKinPartNames,
	                                                  maxNmbEvents,
	                                                  prodKinMomentaLeafName, decayKinMomentaLeafName,
	                                                  debug);
	tree->Write();
	prodKinPartNames->Write (prodKinPartNamesObjName.c_str (), TObject::kSingleKey);
	decayKinPartNames->Write(decayKinPartNamesObjName.c_str(), TObject::kSingleKey);

	outFile->Close();
	if (success)
		printSucc << "wrote events to file '" << outFileName << "'" << endl;
	else
		printWarn << "problems processing events" << endl;
	return success;
}
void
fillUdstDataIntoMassBins_example(const string&      inFileNamePattern = "fillUdstDataIntoMassBins_example.root",
                                 const string&      dirName           = "./test",
                                 const long int     maxNmbEvents      = -1,
                                 const unsigned int nmbMassBins       = 50,
                                 const double       massBinWidth      = 40,   // [MeV/c^2]
                                 const double       massRangeMin      = 500,  // [MeV/c^2]
                                 const string&      uDstTreeName      = "pwaDataTree",
                                 const string&      pwaTreeName       = "rootPwaEvtTree",
                                 const long int     treeCacheSize     = 25000000,  // 25 MByte ROOT tree read cache
                                 const bool         debug             = false)
{
	const string prodKinPartNamesObjName  = "prodKinParticles";
	const string prodKinMomentaLeafName   = "prodKinMomenta";
	const string decayKinPartNamesObjName = "decayKinParticles";
	const string decayKinMomentaLeafName  = "decayKinMomenta";

	TStopwatch timer;
	timer.Start();

	printInfo << "reading uDST file(s) '" << inFileNamePattern << "'" << endl
	          << "    writing " << nmbMassBins << " mass bins in mass interval "
	          << "[" << massRangeMin << ", " << massRangeMin + nmbMassBins * massBinWidth << "] "
	          << "MeV/c^2 to '" << dirName << "'" << endl
	          << "    reading uDST data from tree '" << uDstTreeName << "'" << endl
	          << "    writing PWA data to tree '" << pwaTreeName << "'" << endl;

	// create chain and connect tree leaf variables to branches
	TChain uDstChain(uDstTreeName.c_str());
	if (uDstChain.Add(inFileNamePattern.c_str()) < 1)
		printWarn << "no events in uDST input file(s) '" << inFileNamePattern << "'" << endl;
	const long int nmbEventsUdstChain = uDstChain.GetEntries();
	uDstChain.GetListOfFiles()->ls();

	// !!! <channel-dependent part> !!!
	// connect tree leafs
	TLorentzVector* photons[4] = {0, 0, 0, 0};
	TLorentzVector* piMinus    = 0;
	TLorentzVector* beam       = 0;
	TLorentzVector* recoil     = 0;
	uDstChain.SetBranchAddress("gamma1", &(photons[0]));
	uDstChain.SetBranchAddress("gamma2", &(photons[1]));
	uDstChain.SetBranchAddress("gamma3", &(photons[2]));
	uDstChain.SetBranchAddress("gamma4", &(photons[3]));
	uDstChain.SetBranchAddress("pi_out", &piMinus);
	uDstChain.SetBranchAddress("pi_in",  &beam);
	uDstChain.SetBranchAddress("proton", &recoil);
	uDstChain.SetCacheSize(treeCacheSize);
	uDstChain.AddBranchToCache("gamma1", true);
	uDstChain.AddBranchToCache("gamma2", true);
	uDstChain.AddBranchToCache("gamma3", true);
	uDstChain.AddBranchToCache("gamma4", true);
	uDstChain.AddBranchToCache("pi_out", true);
	uDstChain.AddBranchToCache("pi_in",  true);
	uDstChain.AddBranchToCache("proton", true);
	uDstChain.StopCacheLearningPhase();
	// !!! </channel-dependent part> !!!

	// create directories and .root files
	vector<TFile*> pwaDataFiles;
	vector<TTree*> pwaDataTrees;
	if (not createMassBinFiles(pwaDataFiles, pwaDataTrees, dirName, nmbMassBins, massBinWidth,
	                           massRangeMin, pwaTreeName)) {
		printErr << "there were problems creating the mass bin directories/files. aborting." << endl;
		return;
	}
	printSucc << "created " << pwaDataFiles.size() << " directories/files" << endl;

	// write arrays with production and decay particle names to root files
	{
		TClonesArray prodKinPartNames ("TObjString", 1);
		TClonesArray decayKinPartNames("TObjString", 3);

		// !!! <channel-dependent part> !!!
		new (prodKinPartNames [0]) TObjString("pi-"); // beam particle
		new (decayKinPartNames[0]) TObjString("pi0");
		new (decayKinPartNames[1]) TObjString("pi0");
		new (decayKinPartNames[2]) TObjString("pi-");
		// !!! </channel-dependent part> !!!

		for (unsigned int i = 0; i < pwaDataFiles.size(); ++i) {
			pwaDataFiles[i]->cd();
			prodKinPartNames.Write (prodKinPartNamesObjName.c_str (), TObject::kSingleKey);
			decayKinPartNames.Write(decayKinPartNamesObjName.c_str(), TObject::kSingleKey);
		}
		printSucc << "wrote particle name arrays to all files. "
		          << "beam = 'pi-', decay = {'pi0', 'pi0', 'pi-'}." << endl;
	}

	// create tree leafs
	{
		TClonesArray* prodKinMomenta  = new TClonesArray("TVector3");
		TClonesArray* decayKinMomenta = new TClonesArray("TVector3");
		const int     splitLevel      = 99;
		const int     bufSize         = 256000;
		for (unsigned int i = 0; i < pwaDataTrees.size(); ++i) {
			pwaDataTrees[i]->Branch(prodKinMomentaLeafName.c_str(),  "TClonesArray", &prodKinMomenta,
			                        bufSize, splitLevel);
			pwaDataTrees[i]->Branch(decayKinMomentaLeafName.c_str(), "TClonesArray", &decayKinMomenta,
			                        bufSize, splitLevel);
		}
		printSucc << "created branches for all trees" << endl;
	}

	// loop over events
	const long int nmbEvents = ((maxNmbEvents > 0) ? maxNmbEvents : nmbEventsUdstChain);
	printInfo << "writing events into mass bin files" << endl
	          << "    looping over " << nmbEvents << " tree entries" << endl;
	unsigned long int countEvWritten = 0;
	progress_display  progressIndicator(nmbEvents, cout, "");
	for (long int eventIndex = 0; eventIndex < nmbEvents; ++eventIndex) {
		++progressIndicator;
		if ((uDstChain.LoadTree(eventIndex) < 0) or (uDstChain.GetEntry(eventIndex) == 0)) {
			printWarn << "error reading event " << eventIndex << " from tree. skipping." << endl;
			continue;
		}

		// !!! <channel-dependent part> !!!
		// now just make some minor calculations before
		// construct two pi0s
		const TLorentzVector piZeros[2] = {*(photons[0]) + *(photons[1]), *(photons[2]) + *(photons[3])};
		// construct intermediate state X
		const TLorentzVector X = piZeros[0] + piZeros[1] + *piMinus;
		// calculate t'
		const double t      = (*beam - X) * (*beam - X);
		const double tPrime = fabs(t) - fabs((X.M() * X.M() - beam->M() * beam->M())*(X.M() * X.M() - beam->M() * beam->M())
		                                     / (4 * (beam->Vect() * beam->Vect())));
		// cut on t'
		if ((tPrime < 0.1) or (tPrime > 1.0))
			continue;
		// write out PWA data
		const double         fsEnergy    = X.E() + recoil->E() - recoil->M();  // measured total energy of final state
		const double         scaleFactor = fsEnergy / beam->E();
		const TLorentzVector beamScaled(beam->Vect() * scaleFactor, fsEnergy);
		if (writeEvent(pwaDataTrees, beamScaled, piZeros[0], piZeros[1], *piMinus,
		               X.M(), nmbMassBins, massBinWidth, massRangeMin,
		               prodKinMomentaLeafName, decayKinMomentaLeafName, debug))
			++countEvWritten;
		// !!! </channel-dependent part> !!!
	}

	// write trees
	long unsigned int countTreeEvents = 0;
	for (unsigned int i = 0; i < nmbMassBins; ++i) {
		pwaDataTrees[i]->GetCurrentFile()->Write();
		long unsigned int nmbEvents = pwaDataTrees[i]->GetEntries();
		printSucc << "written " << setw(10) << nmbEvents << " events to file " << "'"
		          << pwaDataTrees[i]->GetCurrentFile()->GetName() << "'" << endl;
		countTreeEvents += nmbEvents;
		pwaDataTrees[i]->GetCurrentFile()->Close();
	}
	pwaDataFiles.clear();
	pwaDataTrees.clear();

	printInfo << "wrote " << min(countEvWritten, countTreeEvents)
	          << " out of " << nmbEvents << " events" <<endl;
	timer.Stop();
	printInfo << "this job consumed: ";
	timer.Print();
}