/// <summary> /// Analyze a tree for a pattern. /// </summary> /// <param name="tree"></param> /// <param name="eventsToAnalyze"># of events to analyze. Zero means the whole tree</param> public List <ArrayGroup> AnalyzeTTree(ROOTClassShell classinfo, ROOTNET.Interface.NTTree tree, int eventsToAnalyze) { if (tree == null) { throw new ArgumentNullException("tree must not be null"); } if (eventsToAnalyze < 0) { throw new ArgumentException("# of events to analyze must be zero ore better."); } /// /// Get the raw numbers out /// var rawnumbers = DetermineAllArrayLengths(classinfo, tree, eventsToAnalyze); /// /// Next, parse the variables into groups /// var groups = DetermineGroups(rawnumbers); return(groups); }
/// <summary> /// Generate a proxy for the tree. This involves making the proxy and then putting in the proper /// text substitutions for later processing by our framework. Some ugly text hacking! /// </summary> /// <param name="tree"></param> /// <returns></returns> private FileInfo MakeProxy(ROOTNET.Interface.NTTree tree) { /// /// First create the proxy. We have to create a darn temp macro name for that. /// var oldDir = Environment.CurrentDirectory; var createItDir = new DirectoryInfo(Environment.CurrentDirectory); if (ProxyGenerationLocation != null) { createItDir = ProxyGenerationLocation; } FileInfo macroFile = new FileInfo(createItDir + @"\junk_macro_parsettree_" + tree.SanitizedName() + ".C"); FileInfo result = null; try { /// /// We can only use local directories /// if (ProxyGenerationLocation != null) { Environment.CurrentDirectory = ProxyGenerationLocation.FullName; } using (var writer = macroFile.CreateText()) { writer.WriteLine("void {0} () {{}}", Path.GetFileNameWithoutExtension(macroFile.Name)); } result = new FileInfo("ntuple_" + tree.SanitizedName() + ".h"); tree.MakeProxy(Path.GetFileNameWithoutExtension(result.Name), macroFile.Name, null, "nohist"); } finally { /// /// Go back to where we were /// Environment.CurrentDirectory = oldDir; } /// /// Next, add the proper things and remove some stuff /// /// /// Return the location of the cpp file /// return(result); }
/// <summary> /// Look at everything we have in this tree and see if we can't generate a class correctly. /// </summary> /// <param name="tree"></param> /// <returns></returns> public IEnumerable <ROOTClassShell> GenerateClasses(ROOTNET.Interface.NTTree tree) { if (tree == null) { throw new ArgumentNullException("tree must not be null"); } /// /// The outlying class is going to be called by the name of the tree. /// var masterClass = new ROOTClassShell(tree.Name); var subClassesByName = from sc in ExtractClassesFromBranchList(masterClass, tree.ListOfBranches.Cast <ROOTNET.Interface.NTBranch>()) group sc by sc.Name; var subClasses = from scg in subClassesByName where scg.ClassesAreIdnetical() select scg.First(); foreach (var sc in subClasses) { yield return(sc); } /// /// Work around a ROOT bug that doesn't allow for unloading of classes /// in the STL after a query is run. Actually, it will unload them, but somehow keeps /// references in memory. /// var f = MakeProxy(tree); masterClass.ClassesToGenerate = ExtractSTLDictionaryReferences(f); /// /// Analyze the TTree arrays. /// var at = new ArrayAnalyzer(); var groupInfo = at.AnalyzeTTree(masterClass, tree, 100); /// /// Any item that isn't an array should be added to the list and /// put in the "ungrouped" array. /// AddNonArrays(masterClass, groupInfo, "ungrouped"); /// /// Write out the user info /// masterClass.UserInfoPath = WriteUserInfo(groupInfo, tree.SanitizedName()).FullName; /// /// Return the master class /// yield return(masterClass); }
/// <summary> /// Returns a tree name sanitized for use as C++ object name. /// </summary> /// <param name="t"></param> /// <returns></returns> public static string SanitizedName(this ROOTNET.Interface.NTTree t) { var n = t.Name; return(n.SanitizedName()); }
/// <summary> /// Loop thorugh a selection of counters and get the sizes /// </summary> /// <param name="counterlist"></param> /// <param name="tree"></param> /// <param name="entry"></param> /// <returns></returns> private Tuple <string, int>[] ComputeCounters(ROOTNET.NTTreeFormula[] counterlist, ROOTNET.Interface.NTTree tree, int entry) { tree.LoadTree(entry); var pairs = from c in counterlist select Tuple.Create(c.Name, (int)c.EvalInstance()); return(pairs.ToArray()); }
/// <summary> /// Run a TTree analysis and get the array sizes for the ntuple. Returns an entry for each /// "event" that is analyzed, with the array names in each one. /// </summary> /// <param name="tree"></param> /// <param name="eventsToAnalyze"></param> /// <returns>The outter index is one per event, and the inner index is for all arrays. Each array has a name and its size in the tuple.</returns> internal Tuple <string, int>[][] DetermineAllArrayLengths(ROOTClassShell classinfo, ROOTNET.Interface.NTTree tree, long eventsToAnalyze) { if (tree.Entries == 0) { return new Tuple <string, int>[][] { new Tuple <string, int> [0] } } ; if (classinfo == null) { throw new ArgumentNullException("the class info cannot be null!"); } /// /// Create the counters /// var arrays = from item in classinfo.Items where item.ItemType.Contains("[]") select item.Name; // + "@.size()" var counterlist = (from item in arrays let treeForm = new ROOTNET.NTTreeFormula(item, string.Format("Length$({0})", item), tree) where treeForm.IsGoodFormula() select treeForm).ToArray(); /// /// Now run through everything /// long entriesToTry = eventsToAnalyze; if (entriesToTry == 0) { entriesToTry = tree.Entries; } if (entriesToTry > tree.Entries) { entriesToTry = tree.Entries; } var eventNtupleData = (from entry in Enumerable.Range(0, (int)entriesToTry) select ComputeCounters(counterlist, tree, entry)).ToArray(); /// /// Make sure to clean up /// foreach (var c in counterlist) { c.Delete(); } return(eventNtupleData); }