3 Build canvases used in validation web pages. 5 Original author: J. Wolcott <jwolcott@fnal.gov> 17 from controllers.ControllerBase
import ControllerBase
18 from models.Organizational
import PlotCollectionKey
19 from models.Organizational
import ComparisonRegistry
20 from models.Organizational
import Configuration
21 from models.Organizational
import PlotID
22 from models.Organizational
import PlotLibrary
23 from models.Organizational
import PlotRegistry
24 from models.Organizational
import PlotSet
25 from models.PlotInfo
import CanvasSummary
26 from models.PlotInfo
import ComparisonSummary
27 from models.PlotInfo
import Exposure
28 from tools.ImageMaker
import ImageMaker
29 from tools
import PathTools
37 if "json" in OUTPUT_TYPES:
40 except AttributeError:
41 OUTPUT_TYPES.remove(
"json")
67 """ Syntactic sugar object to make the setting of a TObject's directory to None easier. 68 If NO_DIR is a NoDirectory instance, write 70 to get back the object with its Directory set to None. 73 if not hasattr(obj,
"SetDirectory"):
76 obj.SetDirectory(
None)
81 """ Syntactic sugar object to make writing a single instance of a warning to stderr 82 (no matter how many times it's called) easier. 91 self._warned.add(warning)
92 print >> sys.stderr,
"\n\n\033[31m\033[1mWarning:\033[0m", warning,
"\n" 97 def __init__(self, accept_filters=[], reject_filters=[]):
105 return not any(name.startswith(flt)
for flt
in self.
reject_filters)
109 for tkey
in tdirectory.GetListOfKeys():
113 if isinstance(obj, ROOT.TH1)
and self.
KeepHist(name):
114 splitted = name.rsplit(
"/", 1)
115 if len(splitted) > 1:
116 path, name = splitted
121 yield base_path + (
"/" if base_path
and path
else "") + path, (NO_DIR >> obj)
124 elif isinstance(obj, ROOT.TDirectory)
and \
125 obj.Get(
"type")
and \
126 obj.Get(
"type").
GetName() ==
"Spectrum" and \
130 "POT": obj.Get(
"pot"),
131 "livetime": obj.Get(
"livetime"),
133 for exp_type, hist
in args.items():
134 args[exp_type] =
None if hist.GetBinContent(1) == 0
else hist.GetBinContent(1)
136 h.SetName(obj.GetName())
139 for label_idx, axis_name
in enumerate(
"XYZ"):
140 title = obj.Get(
"label%d" % label_idx)
142 getattr(h,
"Get%saxis" % axis_name)().
SetTitle(
str(title))
144 yield base_path, (NO_DIR >> h)
146 elif isinstance(obj, ROOT.TDirectory):
147 path = name
if not base_path
else "/".join((base_path, name))
149 yield path, (NO_DIR >> h)
153 """ Helper type to do comparisons to a reference histogram. 154 Used in the display variations within CanvasMaker. """ 156 def __init__(self, comparison_function, ignore_ref=False):
162 ref_val = self.
comp_fn(reference)
163 sub_val = self.
comp_fn(subject)
169 return ref_val/sub_val
if not self.
ignore_ref else sub_val
174 DISPLAY_VARIATIONS = {
176 "enabled": {
"log_y":
True },
177 "disabled": {
"log_y":
False }
186 "norm_factors":
_ReferenceHistComp(
lambda h: 100./h.GetSumOfWeights()
if h.GetSumOfWeights() > 0
else 1, ignore_ref=
True),
187 "axis_labels":
";Percent of entries" 190 "norm_factors":
_ReferenceHistComp(
lambda h: h.exposure.POT
or h.exposure.livetime
if hasattr(h,
"exposure")
and h.exposure
else None)
195 DISPLAY_VARIATIONS_NOJSROOT = [
"Log y",]
198 super(CanvasMaker, self).
__init__(config)
204 reject_filters=self.config.plot_reject_filters)
207 """ Sift through all the files in the configuration and discover histograms. """ 218 for dataset_name, file_list
in self.config.histogram_topdirs.iteritems():
220 if isinstance(file_list, basestring):
221 file_list = [file_list,]
223 for file_name
in file_list:
224 assert file_name.lower().endswith(
".root"),
"Can't open non-ROOT file input '%s'" % file_name
226 f = ROOT.TFile(file_name)
227 assert f,
"Can't open ROOT file '%s'" % file_name
229 for path, histogram
in self.histogram_extractor.ExtractHistsFromDirectory(f):
230 if isinstance(histogram, (ROOT.TH3,)):
231 print >> sys.stderr,
"Don't know how to handle 3D histograms. Skipping histogram:", histogram.GetName()
233 self.
histogram_table[PlotCollectionKey(PlotID(histogram.GetName()), dataset_name, path)] = histogram
236 """ Master method for canvas making. """ 240 self.
im = ImageMaker()
246 overlays = PlotLibrary()
249 by_name_then_dataset = PlotLibrary()
252 for key, histogram
in self.histogram_table.iteritems():
253 if histogram.GetTitle() ==
"":
254 histogram.SetTitle(key.plot_id.name)
257 no_category_key = PlotCollectionKey(
259 "name": key.plot_id.name,
265 by_name_then_dataset.setdefault(no_category_key, {}).setdefault(key.data_set, PlotSet()).add(key)
270 if len(key.plot_id.categories) > 0:
273 categories = key.plot_id.categories[:-1]
274 categories.append(PlotID.OVERLAY_STRING)
276 "name": key.plot_id.name,
277 "categories": categories,
278 "groups": key.plot_id.groups
280 plotid_args[
"categories"] = categories
281 overlay_key = PlotCollectionKey( PlotID(plotid_args), key.data_set, key.path )
282 overlays.setdefault(overlay_key, {})[key.plot_id.categories[-1]] = histogram
285 categories[-1] = PlotID.SUM_STRING
286 plotid_args[
"categories"] = categories
287 sum_key = PlotCollectionKey( PlotID(plotid_args), key.data_set, key.path )
288 if sum_key
not in sums:
289 sum_histogram = histogram.Clone( sum_key.plot_id.id_str )
290 sum_histogram.exposure = histogram.exposure
291 sum_histogram.SetTitle(histogram.GetTitle()
or "Untitled plot")
292 sums[sum_key] = sum_histogram
293 overlays[overlay_key][PlotID.SUM_STRING] = sum_histogram
295 sums[sum_key].
Add(histogram)
297 self.
WriteAndRegisterCanvas({histogram.GetTitle(): histogram}, self.PrepareOutputPath(key.data_set, key.path, key.plot_id.id_str), key)
300 for key, histogram
in sums.iteritems():
302 {histogram.GetTitle(): histogram},
303 self.PrepareOutputPath(key.data_set, key.path, key.plot_id.id_str),
310 for key, histogram_collection
in overlays.iteritems():
312 histogram_collection,
313 self.PrepareOutputPath(key.data_set, key.path, key.plot_id.id_str),
315 plot_order=self.config.plot_order,
328 comparison_dirname = PathTools.ComparisonSubdirName(self.config.histogram_topdirs)
329 for no_category_key, plots_by_dataset
in by_name_then_dataset.iteritems():
330 if len(plots_by_dataset) < 2:
336 for dataset_name, plot_keys
in plots_by_dataset.iteritems():
337 category_lists[dataset_name] = []
338 cl = category_lists[dataset_name]
339 for plot_key
in plot_keys:
340 for cat_idx, cat_name
in enumerate(plot_key.plot_id.categories):
341 if len(cl) < cat_idx + 1:
343 max_num_gps =
max(max_num_gps, cat_idx+1)
344 cl[cat_idx].add(cat_name)
346 identical_keys =
True 347 for gp_idx
in range(max_num_gps):
349 if len(set([len(category_lists[ds])
for ds
in category_lists])) != 1:
350 identical_keys =
False 353 if len(set.union(*[category_lists[ds][gp_idx]
for ds
in category_lists])) != len(category_lists[category_lists.keys()[0]][gp_idx]):
354 identical_keys =
False 358 category_lists = [ set.union(*[cat_list[cat_idx]
for ds, cat_list
in category_lists.iteritems()
if cat_idx < len(cat_list)])
for cat_idx
in range(max_num_gps)]
366 if len(category_lists) > 0
and not identical_keys:
367 del category_lists[-1]
372 for category_combination
in itertools.product(*category_lists):
374 for dataset_name
in plots_by_dataset:
380 reduced_groups = {k: v
for k,v
in plot_key.plot_id.groups.iteritems()
if v < len(categories)}
381 reduced_key = PlotCollectionKey(
383 "name": plot_key.plot_id.name,
384 "categories": categories,
385 "groups": reduced_groups
387 data_set = dataset_name,
388 path=no_category_key.path
398 if len(categories) <= len(category_combination)
and len(categories) > 0
and categories[-1] == PlotID.SUM_STRING:
399 if len(categories) > 0:
404 to_plot[dataset_name] = reduced_key
408 if i >= len(category_combination):
412 categories.append(category_combination[i])
415 categories.append(PlotID.SUM_STRING)
417 categories.insert(-1, category_combination[i])
422 print "Could not find plot for category sequence", category_combination,
"in plot / dataset =", no_category_key.plot_id,
"/", dataset_name
427 if any(isinstance(self.
histogram_table[key], ROOT.TH2)
for key
in to_plot.itervalues())
and not self.config.force_2d_comp:
428 SINGLE_WARN(
"comparisons will not be made for 2D plots. Pass 'force_2d_comp' as true in your config to change.")
434 for key
in to_plot.values():
435 stricter = all(key.plot_id
in other_key.plot_id
for other_key
in to_plot.values()
if other_key != key)
439 assert strictest_key,
"Couldn't find one key that contains all the others in this collection. Keys in collection: %s" % to_plot.values()
448 categories = strictest_key.plot_id.categories
449 if len(strictest_key.plot_id.categories) > 0
and categories[-1] == PlotID.SUM_STRING:
450 categories = strictest_key.plot_id.categories[:-1]
451 comparison_key = PlotCollectionKey(
453 "name": strictest_key.plot_id.name,
454 "categories": categories,
455 "groups": {k: v
for k,v
in strictest_key.plot_id.groups.iteritems()
if v < len(strictest_key.plot_id.categories)-1}
458 path=no_category_key.path
462 plots = {ds: self.
histogram_table[key]
for ds, key
in to_plot.iteritems()}
464 if self.config.ratio_denom
is not None and self.config.ratio_denom
in plots:
465 ratio_denom = self.config.ratio_denom
468 outfile_stub=self.PrepareOutputPath(PathTools.DATASET_COMPARISON_DIRNAME, comparison_dirname, no_category_key.path, comparison_key.plot_id.id_str),
471 plot_order=self.config.plot_order,
472 ratio_denom=ratio_denom
475 if len(to_plot) == 2:
476 c = ComparisonSummary({key: self.
histogram_table[key]
for key
in to_plot.itervalues()})
477 self.comparison_registry.comparisons[comparison_key] = c
479 self.plot_registry.Serialize(self.config.validation_dir)
480 self.comparison_registry.Serialize(self.config.validation_dir)
483 outdir = os.path.dirname(outfile_stub)
484 if not os.path.isdir(outdir):
492 histogram_keys = [p
for p
in plot_order
if p
in histograms]
493 histogram_keys += [p
for p
in reversed(sorted(histograms.keys()))
if p
not in plot_order]
494 if PlotID.SUM_STRING
in histogram_keys:
495 histogram_keys.pop(histogram_keys.index(PlotID.SUM_STRING))
496 histogram_keys.append(PlotID.SUM_STRING)
497 histogram_objs = [histograms[p]
for p
in histogram_keys]
498 ref_idx = histogram_keys.index(ratio_denom)
if ratio_denom
in histogram_keys
else 0
499 sample_hist = histogram_objs[ref_idx]
502 option_groups = CanvasMaker.DISPLAY_VARIATIONS.keys()
503 option_choices = copy.deepcopy(CanvasMaker.DISPLAY_VARIATIONS.values())
509 for choice_list
in option_choices:
510 for choice_opts
in choice_list.itervalues():
511 for arg, val
in choice_opts.items():
513 choice_opts[arg] = [
val(sample_hist, h)
for h
in histogram_objs]
515 for option_choice_set
in itertools.product(*option_choices):
518 if option_choice_set[option_groups.index(
"Normalization")] ==
"exposure" and \
519 not any(hasattr(h,
"exposure")
and h.exposure
for h
in histogram_objs):
523 for suffix
in OUTPUT_TYPES:
526 for option_idx, option_choice_name
in enumerate(option_choice_set):
527 if suffix
in (
"json",
"root")
and option_groups[option_idx]
in CanvasMaker.DISPLAY_VARIATIONS_NOJSROOT:
530 option_string +=
"{group=%s,cat=%s}" % (option_groups[option_idx], option_choice_name)
531 option_args.update(option_choices[option_idx][option_choice_name])
534 "input_histograms": histogram_objs,
535 "ref_hist_idx": ref_idx,
536 "labels": histogram_keys,
537 "colours": GOOD_COLORS,
538 "axis_labels":
"%s;%s" % (sample_hist.GetXaxis().GetTitle(), sample_hist.GetYaxis().GetTitle()),
539 "save_as": outfile_stub + option_string,
540 "suffixes": [
"." + suffix,],
541 "with_ratio": include_ratio,
546 if "axis_labels" in option_args:
547 xaxis_pairs, yaxis_pairs = zip(*(args[
"axis_labels"].
split(
";")
for args
in (option_args, args)))
548 option_args[
"axis_labels"] =
";".join( [update
or default
for update, default
in (xaxis_pairs, yaxis_pairs) ] )
550 args.update(option_args)
552 files_written += self.im.draw(**args)
560 plot_id = PlotID(key.plot_id.id_str + option_string)
561 name = key.plot_id.id_str
if len(histograms) > 1
else histogram_keys[0]
562 info = CanvasSummary(name=name, plots=histogram_objs, labels=histogram_keys)
563 registry_key = PlotCollectionKey(plot_id=plot_id, data_set=
None if include_ratio
else key.data_set, path=files_written[0])
565 self.plot_registry.AddComparisonPlot(registry_key, info)
567 self.plot_registry.AddPlot(registry_key, info)
570 if __name__ ==
"__main__":
571 parser = argparse.ArgumentParser(description=
'Run validation.')
572 parser.add_argument(
'yaml_config', metavar=
'yaml_config',
573 help=
'JSON configuration as specified in README.txt')
575 args = parser.parse_args()
577 config = Configuration.FromYAML(args.yaml_config)
581 print "Building canvases..." 582 print " (base output directory: %s)" % config.validation_dir
584 print "... done. Bye." void split(double tt, double *fr)
def __call__(self, reference, subject)
def WriteAndRegisterCanvas(self, histograms, outfile_stub, key, include_ratio=False, plot_order=[], ratio_denom=None)
gargamelle SetTitle("Gargamelle #nu_{e} CC data")
def __call__(self, warning)
std::string GetName(int i)
def __init__(self, comparison_function, ignore_ref=False)
def __init__(self, config)
def __rshift__(self, obj)
static void Add(TH3D *h, const int bx, const int by, const int bz, const double w)
T max(sqlite3 *const db, std::string const &table_name, std::string const &column_name)