1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """Module for the handling of histograms, including Monte-Carlo error per bin
17 and scale/PDF uncertainties."""
18
19 from __future__ import division
20
21 import array
22 import copy
23 import fractions
24 import itertools
25 import logging
26 import math
27 import os
28 import re
29 import sys
30 import StringIO
31 import subprocess
32 import xml.dom.minidom as minidom
33 from xml.parsers.expat import ExpatError as XMLParsingError
34
35 root_path = os.path.split(os.path.dirname(os.path.realpath( __file__ )))[0]
36 sys.path.append(os.path.join(root_path))
37 sys.path.append(os.path.join(root_path,os.pardir))
38 try:
39
40 import madgraph.various.misc as misc
41 from madgraph import MadGraph5Error
42 logger = logging.getLogger("madgraph.various.histograms")
43
44 except ImportError, error:
45
46 import internal.misc as misc
47 from internal import MadGraph5Error
48 logger = logging.getLogger("internal.histograms")
58 """A class to store lists of physics object."""
59
61 """Exception raised if an error occurs in the definition
62 or execution of a physics object list."""
63 pass
64
66 """Creates a new particle list object. If a list of physics
67 object is given, add them."""
68
69 list.__init__(self)
70
71 if init_list is not None:
72 for object in init_list:
73 self.append(object)
74
76 """Appends an element, but test if valid before."""
77
78 assert self.is_valid_element(object), \
79 "Object %s is not a valid object for the current list" % repr(object)
80
81 list.append(self, object)
82
83
85 """Test if object obj is a valid element for the list."""
86 return True
87
89 """String representation of the physics object list object.
90 Outputs valid Python with improved format."""
91
92 mystr = '['
93
94 for obj in self:
95 mystr = mystr + str(obj) + ',\n'
96
97 mystr = mystr.rstrip(',\n')
98
99 return mystr + ']'
100
101
102 -class Bin(object):
103 """A class to store Bin related features and function.
104 """
105
106 - def __init__(self, boundaries=(0.0,0.0), wgts=None, n_entries = 0):
107 """ Initializes an empty bin, necessarily with boundaries. """
108
109 self.boundaries = boundaries
110 self.n_entries = n_entries
111 if not wgts:
112 self.wgts = {'central':0.0}
113 else:
114 self.wgts = wgts
115
117 if name=='boundaries':
118 if not isinstance(value, tuple):
119 raise MadGraph5Error, "Argument '%s' for bin property "+\
120 "'boundaries' must be a tuple."%str(value)
121 else:
122 for coordinate in value:
123 if isinstance(coordinate, tuple):
124 for dim in coordinate:
125 if not isinstance(dim, float):
126 raise MadGraph5Error, "Coordinate '%s' of the bin"+\
127 " boundary '%s' must be a float."%str(dim,value)
128 elif not isinstance(coordinate, float):
129 raise MadGraph5Error, "Element '%s' of the bin boundaries"+\
130 " specified must be a float."%str(bound)
131 elif name=='wgts':
132 if not isinstance(value, dict):
133 raise MadGraph5Error, "Argument '%s' for bin uncertainty "+\
134 "'wgts' must be a dictionary."%str(value)
135 for val in value.values():
136 if not isinstance(val,float):
137 raise MadGraph5Error, "The bin weight value '%s' is not a "+\
138 "float."%str(val)
139
140 super(Bin, self).__setattr__(name,value)
141
143 """ Accesses a specific weight from this bin."""
144 try:
145 return self.wgts[key]
146 except KeyError:
147 raise MadGraph5Error, "Weight with ID '%s' is not defined for"+\
148 " this bin"%str(key)
149
151 """ Accesses a specific weight from this bin."""
152
153
154
155 assert(isinstance(wgt, float))
156
157 try:
158 self.wgts[key] = wgt
159 except KeyError:
160 raise MadGraph5Error, "Weight with ID '%s' is not defined for"+\
161 " this bin"%str(key)
162
164 """ Add an event to this bin. """
165
166
167 if isinstance(weights, float):
168 weights = {'central': weights}
169
170 for key in weights:
171 if key == 'stat_error':
172 continue
173 try:
174 self.wgts[key] += weights[key]
175 except KeyError:
176 raise MadGraph5Error('The event added defines the weight '+
177 '%s which was not '%key+'registered in this histogram.')
178
179 self.n_entries += 1
180
181
182
183
184
185
186
188 """ Nice representation of this Bin.
189 One can order the weight according to the argument if provided."""
190
191 res = ["Bin boundaries : %s"%str(self.boundaries)]
192 if not short:
193 res.append("Bin weights :")
194 if order is None:
195 label_list = self.wgts.keys()
196 else:
197 label_list = order
198
199 for label in label_list:
200 try:
201 res.append(" -> '%s' : %4.3e"%(str(label),self.wgts[label]))
202 except KeyError:
203 pass
204 else:
205 res.append("Central weight : %4.3e"%self.get_weight())
206
207 return '\n'.join(res)
208
210 """ Apply a given function to all bin weights."""
211 self.wgts = func(self.wgts)
212
213 @classmethod
214 - def combine(cls, binA, binB, func):
215 """ Function to combine two bins. The 'func' is such that it takes
216 two weight dictionaries and merge them into one."""
217
218 res_bin = cls()
219 if binA.boundaries != binB.boundaries:
220 raise MadGraph5Error, 'The two bins to combine have'+\
221 ' different boundaries, %s!=%s.'%(str(binA.boundaries),str(binB.boundaries))
222 res_bin.boundaries = binA.boundaries
223
224 try:
225 res_bin.wgts = func(binA.wgts, binB.wgts)
226 except Exception as e:
227 raise MadGraph5Error, "When combining two bins, the provided"+\
228 " function '%s' triggered the following error:\n\"%s\"\n"%\
229 (func.__name__,str(e))+" when combining the following two bins:\n"+\
230 binA.nice_string(short=False)+"\n and \n"+binB.nice_string(short=False)
231
232 return res_bin
233
234 -class BinList(histograms_PhysicsObjectList):
235 """ A class implementing features related to a list of Bins. """
236
237 - def __init__(self, list = [], bin_range = None,
238 weight_labels = None):
239 """ Initialize a list of Bins. It is possible to define the range
240 as a list of three floats: [min_x, max_x, bin_width]"""
241
242 self.weight_labels = weight_labels
243 if bin_range:
244
245 if not self.weight_labels:
246 self.weight_labels = ['central', 'stat_error']
247 if len(bin_range)!=3 or any(not isinstance(f, float) for f in bin_range):
248 raise MadGraph5Error, "The range argument to build a BinList"+\
249 " must be a list of exactly three floats."
250 current = bin_range[0]
251 while current < bin_range[1]:
252 self.append(Bin(boundaries =
253 (current, min(current+bin_range[2],bin_range[1])),
254 wgts = dict((wgt,0.0) for wgt in self.weight_labels)))
255 current += bin_range[2]
256 else:
257 super(BinList, self).__init__(list)
258
260 """Test whether specified object is of the right type for this list."""
261
262 return isinstance(obj, Bin)
263
265 if name=='weight_labels':
266 if not value is None and not isinstance(value, list):
267 raise MadGraph5Error, "Argument '%s' for BinList property '%s'"\
268 %(str(value),name)+' must be a list.'
269 elif not value is None:
270 for label in value:
271 if all((not isinstance(label,cls)) for cls in \
272 [str, int, float, tuple]):
273 raise MadGraph5Error, "Element '%s' of the BinList property '%s'"\
274 %(str(value),name)+' must be a string, an '+\
275 'integer, a float or a tuple of float.'
276 if isinstance(label, tuple):
277 if len(label)>=1:
278 if not isinstance(label[0], (float, str)):
279 raise MadGraph5Error, "Argument "+\
280 "'%s' for BinList property '%s'"%(str(value),name)+\
281 ' can be a tuple, but its first element must be a float or string.'
282 for elem in label[1:]:
283 if not isinstance(elem, (float,int,str)):
284 raise MadGraph5Error, "Argument "+\
285 "'%s' for BinList property '%s'"%(str(value),name)+\
286 ' can be a tuple, but its elements past the first one must be either floats, integers or strings'
287
288
289 super(BinList, self).__setattr__(name, value)
290
292 """Appends an element, but test if valid before."""
293
294 super(BinList,self).append(object)
295
296 if len(self)==1 and self.weight_labels is None:
297 self.weight_labels = object.wgts.keys()
298
300 """ Nice representation of this BinList."""
301
302 res = ["Number of bin in the list : %d"%len(self)]
303 res.append("Registered weight labels : [%s]"%(', '.join([
304 str(label) for label in self.weight_labels])))
305 if not short:
306 for i, bin in enumerate(self):
307 res.append('Bin number %d :'%i)
308 res.append(bin.nice_string(order=self.weight_labels, short=short))
309
310 return '\n'.join(res)
311
313 """A mother class for all specific implementations of Histogram conventions
314 """
315
316 allowed_dimensions = None
317 allowed_types = []
318 allowed_axis_modes = ['LOG','LIN']
319
320 - def __init__(self, title = "NoName", n_dimensions = 2, type=None,
321 x_axis_mode = 'LIN', y_axis_mode = 'LOG', bins=None):
322 """ Initializes an empty histogram, possibly specifying
323 > a title
324 > a number of dimensions
325 > a bin content
326 """
327
328 self.title = title
329 self.dimension = n_dimensions
330 if not bins:
331 self.bins = BinList([])
332 else:
333 self.bins = bins
334 self.type = type
335 self.x_axis_mode = x_axis_mode
336 self.y_axis_mode = y_axis_mode
337
339 if name=='title':
340 if not isinstance(value, str):
341 raise MadGraph5Error, "Argument '%s' for the histogram property "+\
342 "'title' must be a string."%str(value)
343 elif name=='dimension':
344 if not isinstance(value, int):
345 raise MadGraph5Error, "Argument '%s' for histogram property "+\
346 "'dimension' must be an integer."%str(value)
347 if self.allowed_dimensions and value not in self.allowed_dimensions:
348 raise MadGraph5Error, "%i-Dimensional histograms not supported "\
349 %value+"by class '%s'. Supported dimensions are '%s'."\
350 %(self.__class__.__name__,self.allowed_dimensions)
351 elif name=='bins':
352 if not isinstance(value, BinList):
353 raise MadGraph5Error, "Argument '%s' for histogram property "+\
354 "'bins' must be a BinList."%str(value)
355 else:
356 for bin in value:
357 if not isinstance(bin, Bin):
358 raise MadGraph5Error, "Element '%s' of the "%str(bin)+\
359 " histogram bin list specified must be a bin."
360 elif name=='type':
361 if not (value is None or value in self.allowed_types or
362 self.allowed_types==[]):
363 raise MadGraph5Error, "Argument '%s' for histogram"%str(value)+\
364 " property 'type' must be a string in %s or None."\
365 %([str(t) for t in self.allowed_types])
366 elif name in ['x_axis_mode','y_axis_mode']:
367 if not value in self.allowed_axis_modes:
368 raise MadGraph5Error, "Attribute '%s' of the histogram"%str(name)+\
369 " must be in [%s], ('%s' given)"%(str(self.allowed_axis_modes),
370 str(value))
371
372 super(Histogram, self).__setattr__(name,value)
373
375 """ Nice representation of this histogram. """
376
377 res = ['<%s> histogram:'%self.__class__.__name__]
378 res.append(' -> title : "%s"'%self.title)
379 res.append(' -> dimensions : %d'%self.dimension)
380 if not self.type is None:
381 res.append(' -> type : %s'%self.type)
382 else:
383 res.append(' -> type : None')
384 res.append(' -> (x, y)_axis : ( %s, %s)'%\
385 (tuple([('Linear' if mode=='LIN' else 'Logarithmic') for mode in \
386 [self.x_axis_mode, self.y_axis_mode]])))
387 if short:
388 res.append(' -> n_bins : %s'%len(self.bins))
389 res.append(' -> weight types : [ %s ]'%
390 (', '.join([str(label) for label in self.bins.weight_labels]) \
391 if (not self.bins.weight_labels is None) else 'None'))
392
393 else:
394 res.append(' -> Bins content :')
395 res.append(self.bins.nice_string(short))
396
397 return '\n'.join(res)
398
400 """ Apply a given function to all bin weights."""
401
402 for bin in self.bins:
403 bin.alter_weights(func)
404
405 @classmethod
406 - def combine(cls, histoA, histoB, func):
407 """ Function to combine two Histograms. The 'func' is such that it takes
408 two weight dictionaries and merge them into one."""
409
410 res_histogram = copy.copy(histoA)
411 if histoA.title != histoB.title:
412 res_histogram.title = "[%s]__%s__[%s]"%(histoA.title,func.__name__,
413 histoB.title)
414 else:
415 res_histogram.title = histoA.title
416
417 res_histogram.bins = BinList([])
418 if len(histoA.bins)!=len(histoB.bins):
419 raise MadGraph5Error, 'The two histograms to combine have a '+\
420 'different number of bins, %d!=%d.'%(len(histoA.bins),len(histoB.bins))
421
422 if histoA.dimension!=histoB.dimension:
423 raise MadGraph5Error, 'The two histograms to combine have a '+\
424 'different dimensions, %d!=%d.'%(histoA.dimension,histoB.dimension)
425 res_histogram.dimension = histoA.dimension
426
427 for i, bin in enumerate(histoA.bins):
428 res_histogram.bins.append(Bin.combine(bin, histoB.bins[i],func))
429
430
431
432 res_histogram.bins.weight_labels = [label for label in histoA.bins.\
433 weight_labels if label in res_histogram.bins.weight_labels] + \
434 sorted([label for label in res_histogram.bins.weight_labels if\
435 label not in histoA.bins.weight_labels])
436
437
438 return res_histogram
439
440
441
442
443 @staticmethod
445 """ Apply the multiplication to the weights of two bins."""
446
447 new_wgts = {}
448
449 new_wgts['stat_error'] = math.sqrt(
450 (wgtsA['stat_error']*wgtsB['central'])**2+
451 (wgtsA['central']*wgtsB['stat_error'])**2)
452
453 for label, wgt in wgtsA.items():
454 if label=='stat_error':
455 continue
456 new_wgts[label] = wgt*wgtsB[label]
457
458 return new_wgts
459
460 @staticmethod
462 """ Apply the division to the weights of two bins."""
463
464 new_wgts = {}
465 if wgtsB['central'] == 0.0:
466 new_wgts['stat_error'] = 0.0
467 else:
468
469 new_wgts['stat_error'] = math.sqrt(wgtsA['stat_error']**2+
470 ((wgtsA['central']*wgtsB['stat_error'])/
471 wgtsB['central'])**2)/wgtsB['central']
472
473 for label, wgt in wgtsA.items():
474 if label=='stat_error':
475 continue
476 if wgtsB[label]==0.0 and wgt==0.0:
477 new_wgts[label] = 0.0
478 elif wgtsB[label]==0.0:
479
480
481
482
483 new_wgts[label] = 0.0
484 else:
485 new_wgts[label] = wgt/wgtsB[label]
486
487 return new_wgts
488
489 @staticmethod
490 - def OPERATION(wgtsA, wgtsB, wgt_operation, stat_error_operation):
491 """ Apply the operation to the weights of two bins. Notice that we
492 assume here the two dict operands to have the same weight labels.
493 The operation is a function that takes two floats as input."""
494
495 new_wgts = {}
496 for label, wgt in wgtsA.items():
497 if label!='stat_error':
498 new_wgts[label] = wgt_operation(wgt, wgtsB[label])
499 else:
500 new_wgts[label] = stat_error_operation(wgt, wgtsB[label])
501
502
503
504
505
506 return new_wgts
507
508
509 @staticmethod
511 """ Apply the operation to the weights of a *single* bins.
512 The operation is a function that takes a single float as input."""
513
514 new_wgts = {}
515 for label, wgt in wgts.items():
516 if label!='stat_error':
517 new_wgts[label] = wgt_operation(wgt)
518 else:
519 new_wgts[label] = stat_error_operation(wgt)
520
521 return new_wgts
522
523 @staticmethod
524 - def ADD(wgtsA, wgtsB):
525 """ Implements the addition using OPERATION above. """
526 return Histogram.OPERATION(wgtsA, wgtsB,
527 (lambda a,b: a+b),
528 (lambda a,b: math.sqrt(a**2+b**2)))
529
530 @staticmethod
532 """ Implements the subtraction using OPERATION above. """
533
534 return Histogram.OPERATION(wgtsA, wgtsB,
535 (lambda a,b: a-b),
536 (lambda a,b: math.sqrt(a**2+b**2)))
537
538 @staticmethod
540 """ Implements the rescaling using SINGLEHISTO_OPERATION above. """
541
542 def rescaler(wgts):
543 return Histogram.SINGLEHISTO_OPERATION(wgts,(lambda a: a*factor),
544 (lambda a: a*factor))
545
546 return rescaler
547
548 @staticmethod
550 """ Implements the offset using SINGLEBIN_OPERATION above. """
551 def offsetter(wgts):
552 return Histogram.SINGLEHISTO_OPERATION(
553 wgts,(lambda a: a+offset),(lambda a: a))
554
555 return offsetter
556
558 """ Overload the plus function. """
559 if isinstance(other, Histogram):
560 return self.__class__.combine(self,other,Histogram.ADD)
561 elif isinstance(other, int) or isinstance(other, float):
562 self.alter_weights(Histogram.OFFSET(float(other)))
563 return self
564 else:
565 return NotImplemented, 'Histograms can only be added to other '+\
566 ' histograms or scalars.'
567
569 """ Overload the subtraction function. """
570 if isinstance(other, Histogram):
571 return self.__class__.combine(self,other,Histogram.SUBTRACT)
572 elif isinstance(other, int) or isinstance(other, float):
573 self.alter_weights(Histogram.OFFSET(-float(other)))
574 return self
575 else:
576 return NotImplemented, 'Histograms can only be subtracted to other '+\
577 ' histograms or scalars.'
578
580 """ Overload the multiplication function. """
581 if isinstance(other, Histogram):
582 return self.__class__.combine(self,other,Histogram.MULTIPLY)
583 elif isinstance(other, int) or isinstance(other, float):
584 self.alter_weights(Histogram.RESCALE(float(other)))
585 return self
586 else:
587 return NotImplemented, 'Histograms can only be multiplied to other '+\
588 ' histograms or scalars.'
589
591 """ Overload the multiplication function. """
592 if isinstance(other, Histogram):
593 return self.__class__.combine(self,other,Histogram.DIVIDE)
594 elif isinstance(other, int) or isinstance(other, float):
595 self.alter_weights(Histogram.RESCALE(1.0/float(other)))
596 return self
597 else:
598 return NotImplemented, 'Histograms can only be divided with other '+\
599 ' histograms or scalars.'
600
601 __truediv__ = __div__
602
603 -class HwU(Histogram):
604 """A concrete implementation of an histogram plots using the HwU format for
605 reading/writing histogram content."""
606
607 allowed_dimensions = [2]
608 allowed_types = []
609
610
611 output_formats_implemented = ['HwU','gnuplot']
612
613
614
615 mandatory_weights = {'xmin':'boundary_xmin', 'xmax':'boundary_xmax',
616 'central value':'central', 'dy':'stat_error'}
617
618
619
620
621
622 weight_header_start_re = re.compile('^##.*')
623
624
625
626 weight_header_re = re.compile(
627 '&\s*(?P<wgt_name>(\S|(\s(?!\s*(&|$))))+)(\s(?!(&|$)))*')
628
629
630
631
632
633 histo_start_re = re.compile('^\s*<histogram>\s*(?P<n_bins>\d+)\s*"\s*'+
634 '(?P<histo_name>(\S|(\s(?!\s*")))+)\s*"\s*$')
635
636 a_float_re = '[\+|-]?\d+(\.\d*)?([EeDd][\+|-]?\d+)?'
637 histo_bin_weight_re = re.compile('(?P<weight>%s|NaN)'%a_float_re,re.IGNORECASE)
638 a_int_re = '[\+|-]?\d+'
639
640
641 histo_end_re = re.compile(r'^\s*<\\histogram>\s*$')
642
643 weight_label_scale = re.compile('^\s*mur\s*=\s*(?P<mur_fact>%s)'%a_float_re+\
644 '\s*muf\s*=\s*(?P<muf_fact>%s)\s*$'%a_float_re,re.IGNORECASE)
645 weight_label_PDF = re.compile('^\s*PDF\s*=\s*(?P<PDF_set>\d+)\s*$')
646 weight_label_PDF_XML = re.compile('^\s*pdfset\s*=\s*(?P<PDF_set>\d+)\s*$')
647 weight_label_TMS = re.compile('^\s*TMS\s*=\s*(?P<Merging_scale>%s)\s*$'%a_float_re)
648 weight_label_alpsfact = re.compile('^\s*alpsfact\s*=\s*(?P<alpsfact>%s)\s*$'%a_float_re,
649 re.IGNORECASE)
650
651 weight_label_scale_adv = re.compile('^\s*dyn\s*=\s*(?P<dyn_choice>%s)'%a_int_re+\
652 '\s*mur\s*=\s*(?P<mur_fact>%s)'%a_float_re+\
653 '\s*muf\s*=\s*(?P<muf_fact>%s)\s*$'%a_float_re,re.IGNORECASE)
654 weight_label_PDF_adv = re.compile('^\s*PDF\s*=\s*(?P<PDF_set>\d+)\s+(?P<PDF_set_cen>\S+)\s*$')
655
656
658 """a class for histogram data parsing errors"""
659
660 @classmethod
662 """ From the format of the weight label given in argument, it returns
663 a string identifying the type of standard weight it is."""
664
665 if isinstance(wgt_label,str):
666 return 'UNKNOWN_TYPE'
667 if isinstance(wgt_label,tuple):
668 if len(wgt_label)==0:
669 return 'UNKNOWN_TYPE'
670 if isinstance(wgt_label[0],float):
671 return 'murmuf_scales'
672 if isinstance(wgt_label[0],str):
673 return wgt_label[0]
674 if isinstance(wgt_label,float):
675 return 'merging_scale'
676 if isinstance(wgt_label,int):
677 return 'pdfset'
678
679 return 'UNKNOWN_TYPE'
680
681
682 - def __init__(self, file_path=None, weight_header=None,
683 raw_labels=False, consider_reweights='ALL', selected_central_weight=None, **opts):
684 """ Read one plot from a file_path or a stream. Notice that this
685 constructor only reads one, and the first one, of the plots specified.
686 If file_path was a path in argument, it would then close the opened stream.
687 If file_path was a stream in argument, it would leave it open.
688 The option weight_header specifies an ordered list of weight names
689 to appear in the file specified.
690 The option 'raw_labels' specifies that one wants to import the
691 histogram data with no treatment of the weight labels at all
692 (this is used for the matplotlib output)."""
693
694 super(HwU, self).__init__(**opts)
695
696 self.dimension = 2
697
698 if file_path is None:
699 return
700 elif isinstance(file_path, str):
701 stream = open(file_path,'r')
702 elif isinstance(file_path, file):
703 stream = file_path
704 else:
705 raise MadGraph5Error, "Argument file_path '%s' for HwU init"\
706 %str(file_path)+"ialization must be either a file path or a stream."
707
708
709 if not weight_header:
710 weight_header = HwU.parse_weight_header(stream, raw_labels=raw_labels)
711
712 if not self.parse_one_histo_from_stream(stream, weight_header,
713 consider_reweights=consider_reweights,
714 selected_central_weight=selected_central_weight,
715 raw_labels=raw_labels):
716
717
718 super(Histogram,self).__setattr__('bins',None)
719
720
721 if isinstance(file_path, str):
722 stream.close()
723
724 - def addEvent(self, x_value, weights = 1.0):
725 """ Add an event to the current plot. """
726
727 for bin in self.bins:
728 if bin.boundaries[0] <= x_value < bin.boundaries[1]:
729 bin.addEvent(weights = weights)
730
731 - def get(self, name):
732
733 if name == 'bins':
734 return [b.boundaries[0] for b in self.bins]
735 else:
736 return [b.wgts[name] for b in self.bins]
737
755
757 """return two list of entry one with the minimum and one with the maximum value.
758 selector can be:
759 - a regular expression on the label name
760 - a function returning T/F (applying on the label name)
761 - a list of labels
762 - a keyword
763 """
764
765
766 if isinstance(selector, str):
767 if selector == 'QCUT':
768 selector = r'^Weight_MERGING=[\d]*[.]?\d*$'
769 elif selector == 'SCALE':
770 selector = r'(MUF=\d*[.]?\d*_MUR=([^1]\d*|1\d+)_PDF=\d*)[.]?\d*|(MUF=([^1]\d*|1\d+)[.]?\d*_MUR=\d*[.]?\d*_PDF=\d*)'
771 elif selector == 'ALPSFACT':
772 selector = r'ALPSFACT'
773 elif selector == 'PDF':
774 selector = r'(?:MUF=1_MUR=1_PDF=|MU(?:F|R)="1.0" MU(?:R|F)="1.0" PDF=")(\d*)'
775 if not mode:
776
777
778
779
780
781 pdfs = [int(re.findall(selector, n)[0]) for n in self.bins[0].wgts if re.search(selector,n, re.IGNORECASE)]
782 min_pdf, max_pdf = min(pdfs), max(pdfs)
783 if max_pdf - min_pdf > 100:
784 mode == 'min/max'
785 elif max_pdf <= 90000:
786 mode = 'hessian'
787 else:
788 mode = 'gaussian'
789 selections = [n for n in self.bins[0].wgts if re.search(selector,n, re.IGNORECASE)]
790 elif hasattr(selector, '__call__'):
791 selections = [n for n in self.bins[0].wgts if selector(n)]
792 elif isinstance(selector, (list, tuple)):
793 selections = selector
794
795
796 if not mode:
797 mode = 'min/max'
798
799
800 values = []
801 for s in selections:
802 values.append(self.get(s))
803
804
805 if not len(values):
806 return [0] * len(self.bins), [0]* len(self.bins)
807 elif len(values) ==1:
808 return values[0], values[0]
809
810
811
812 if mode == 'min/max':
813 min_value, max_value = [], []
814 for i in xrange(len(values[0])):
815 data = [values[s][i] for s in xrange(len(values))]
816 min_value.append(min(data))
817 max_value.append(max(data))
818 elif mode == 'gaussian':
819
820 min_value, max_value = [], []
821 for i in xrange(len(values[0])):
822 pdf_stdev = 0.0
823 data = [values[s][i] for s in xrange(len(values))]
824 sdata = sum(data)/len(data)
825 sdata2 = sum(x**2 for x in data)/len(data)
826 pdf_stdev = math.sqrt(max(sdata2 -sdata**2,0.0))
827 min_value.append(sdata - pdf_stdev)
828 max_value.append(sdata + pdf_stdev)
829
830 elif mode == 'hessian':
831
832
833 pdfs = [(int(re.findall(selector, n)[0]),n) for n in self.bins[0].wgts if re.search(selector,n, re.IGNORECASE)]
834 pdfs.sort()
835
836
837 if len(pdfs) % 2:
838
839 pdf1 = pdfs[0][0]
840 central = pdf1 -1
841 name = pdfs[0][1].replace(str(pdf1), str(central))
842 central = self.get(name)
843 else:
844 central = self.get(pdfs.pop(0)[1])
845
846
847 values = []
848 for _, name in pdfs:
849 values.append(self.get(name))
850
851
852 min_value, max_value = [], []
853 for i in xrange(len(values[0])):
854 pdf_up = 0
855 pdf_down = 0
856 cntrl_val = central[i]
857 for s in range(int((len(pdfs))/2)):
858 pdf_up += max(0.0,values[2*s][i] - cntrl_val,
859 values[2*s+1][i] - cntrl_val)**2
860 pdf_down += max(0.0,cntrl_val - values[2*s][i],
861 cntrl_val - values[2*s+1][i])**2
862
863 min_value.append(cntrl_val - math.sqrt(pdf_down))
864 max_value.append(cntrl_val + math.sqrt(pdf_up))
865
866
867
868
869 return min_value, max_value
870
900
902 """ Returns the string representation of this histogram using the
903 HwU standard."""
904
905 res = []
906 if print_header:
907 res.append(self.get_formatted_header())
908 res.extend([''])
909 res.append('<histogram> %s "%s"'%(len(self.bins),
910 self.get_HwU_histogram_name(format='HwU')))
911 for bin in self.bins:
912 if 'central' in bin.wgts:
913 res.append(' '.join('%+16.7e'%wgt for wgt in list(bin.boundaries)+
914 [bin.wgts['central'],bin.wgts['stat_error']]))
915 else:
916 res.append(' '.join('%+16.7e'%wgt for wgt in list(bin.boundaries)))
917 res[-1] += ' '.join('%+16.7e'%bin.wgts[key] for key in
918 self.bins.weight_labels if key not in ['central','stat_error'])
919 res.append('<\histogram>')
920 return res
921
922 - def output(self, path=None, format='HwU', print_header=True):
923 """ Ouput this histogram to a file, stream or string if path is kept to
924 None. The supported format are for now. Chose whether to print the header
925 or not."""
926
927 if not format in HwU.output_formats_implemented:
928 raise MadGraph5Error, "The specified output format '%s'"%format+\
929 " is not yet supported. Supported formats are %s."\
930 %HwU.output_formats_implemented
931
932 if format == 'HwU':
933 str_output_list = self.get_HwU_source(print_header=print_header)
934
935 if path is None:
936 return '\n'.join(str_output_list)
937 elif isinstance(path, str):
938 stream = open(path,'w')
939 stream.write('\n'.join(str_output_list))
940 stream.close()
941 elif isinstance(path, file):
942 path.write('\n'.join(str_output_list))
943
944
945 return True
946
949 """ Test whether the defining attributes of self are identical to histo,
950 typically to make sure that they are the same plots but from different
951 runs, and they can be summed safely. We however don't want to
952 overload the __eq__ because it is still a more superficial check."""
953
954 this_known_weight_labels = [label for label in self.bins.weight_labels if
955 HwU.get_HwU_wgt_label_type(label)!='UNKNOWN_TYPE']
956 other_known_weight_labels = [label for label in other.bins.weight_labels if
957 HwU.get_HwU_wgt_label_type(label)!='UNKNOWN_TYPE']
958 this_unknown_weight_labels = [label for label in self.bins.weight_labels if
959 HwU.get_HwU_wgt_label_type(label)=='UNKNOWN_TYPE']
960 other_unknown_weight_labels = [label for label in other.bins.weight_labels if
961 HwU.get_HwU_wgt_label_type(label)=='UNKNOWN_TYPE']
962
963 if self.title != other.title or \
964 set(this_known_weight_labels) != set(other_known_weight_labels) or \
965 (set(this_unknown_weight_labels) != set(other_unknown_weight_labels) and\
966 consider_unknown_weight_labels) or \
967 (self.type != other.type and consider_type) or \
968 self.x_axis_mode != self.x_axis_mode or \
969 self.y_axis_mode != self.y_axis_mode or \
970 any(b1.boundaries!=b2.boundaries for (b1,b2) in \
971 zip(self.bins,other.bins)):
972 return False
973
974 return True
975
976
977
978 @classmethod
980 """ Read a given stream until it finds a header specifying the weights
981 and then returns them."""
982
983 for line in stream:
984 if cls.weight_header_start_re.match(line):
985 header = [h.group('wgt_name') for h in
986 cls.weight_header_re.finditer(line)]
987 if any((name not in header) for name in cls.mandatory_weights):
988 raise HwU.ParseError, "The mandatory weight names %s were"\
989 %str(cls.mandatory_weights.keys())+" are not all present"+\
990 " in the following HwU header definition:\n %s"%line
991
992
993 if raw_labels:
994
995
996 header = [ (h if h not in ['xmin','xmax'] else
997 cls.mandatory_weights[h]) for h in header ]
998
999 return header
1000 else:
1001 header = [ (h if h not in cls.mandatory_weights else
1002 cls.mandatory_weights[h]) for h in header ]
1003
1004
1005
1006
1007 for i, h in enumerate(header):
1008 scale_wgt = HwU.weight_label_scale.match(h)
1009 PDF_wgt = HwU.weight_label_PDF.match(h)
1010 Merging_wgt = HwU.weight_label_TMS.match(h)
1011 alpsfact_wgt = HwU.weight_label_alpsfact.match(h)
1012 scale_wgt_adv = HwU.weight_label_scale_adv.match(h)
1013 PDF_wgt_adv = HwU.weight_label_PDF_adv.match(h)
1014 if scale_wgt_adv:
1015 header[i] = ('scale_adv',
1016 int(scale_wgt_adv.group('dyn_choice')),
1017 float(scale_wgt_adv.group('mur_fact')),
1018 float(scale_wgt_adv.group('muf_fact')))
1019 elif scale_wgt:
1020 header[i] = ('scale',
1021 float(scale_wgt.group('mur_fact')),
1022 float(scale_wgt.group('muf_fact')))
1023 elif PDF_wgt_adv:
1024 header[i] = ('pdf_adv',
1025 int(PDF_wgt_adv.group('PDF_set')),
1026 PDF_wgt_adv.group('PDF_set_cen'))
1027 elif PDF_wgt:
1028 header[i] = ('pdf',int(PDF_wgt.group('PDF_set')))
1029 elif Merging_wgt:
1030 header[i] = ('merging_scale',float(Merging_wgt.group('Merging_scale')))
1031 elif alpsfact_wgt:
1032 header[i] = ('alpsfact',float(alpsfact_wgt.group('alpsfact')))
1033
1034 return header
1035
1036 raise HwU.ParseError, "The weight headers could not be found."
1037
1038
1040 """ Parse the histogram name for tags which would set its various
1041 attributes."""
1042
1043 for i, tag in enumerate(histogram_name.split('|')):
1044 if i==0:
1045 self.title = tag.strip()
1046 else:
1047 stag = tag.split('@')
1048 if len(stag)==1 and stag[0].startswith('#'): continue
1049 if len(stag)!=2:
1050 raise MadGraph5Error, 'Specifier in title must have the'+\
1051 " syntax @<attribute_name>:<attribute_value>, not '%s'."%tag.strip()
1052
1053 stag = [t.strip().upper() for t in stag]
1054 if stag[0] in ['T','TYPE']:
1055 self.type = stag[1]
1056 elif stag[0] in ['X_AXIS', 'X']:
1057 self.x_axis_mode = stag[1]
1058 elif stag[0] in ['Y_AXIS', 'Y']:
1059 self.y_axis_mode = stag[1]
1060 elif stag[0] in ['JETSAMPLE', 'JS']:
1061 self.jetsample = int(stag[1])
1062 else:
1063 raise MadGraph5Error, "Specifier '%s' not recognized."%stag[0]
1064
1066 """ Returns the histogram name in the HwU syntax or human readable."""
1067
1068 type_map = {'NLO':'NLO', 'LO':'LO', 'AUX':'auxiliary histogram'}
1069
1070 if format=='human':
1071 res = self.title
1072 if not self.type is None:
1073 try:
1074 res += ', %s'%type_map[self.type]
1075 except KeyError:
1076 res += ', %s'%str('NLO' if self.type.split()[0]=='NLO' else
1077 self.type)
1078 if hasattr(self,'jetsample'):
1079 if self.jetsample==-1:
1080 res += ', all jet samples'
1081 else:
1082 res += ', Jet sample %d'%self.jetsample
1083
1084 return res
1085
1086 elif format=='human-no_type':
1087 res = self.title
1088 return res
1089
1090 elif format=='HwU':
1091 res = [self.title]
1092 res.append('|X_AXIS@%s'%self.x_axis_mode)
1093 res.append('|Y_AXIS@%s'%self.y_axis_mode)
1094 if hasattr(self,'jetsample'):
1095 res.append('|JETSAMPLE@%d'%self.jetsample)
1096 if self.type:
1097 res.append('|TYPE@%s'%self.type)
1098 return ' '.join(res)
1099
1100 - def parse_one_histo_from_stream(self, stream, all_weight_header,
1101 consider_reweights='ALL', raw_labels=False, selected_central_weight=None):
1102 """ Reads *one* histogram from a stream, with the mandatory specification
1103 of the ordered list of weight names. Return True or False depending
1104 on whether the starting definition of a new plot could be found in this
1105 stream."""
1106 n_bins = 0
1107
1108 if consider_reweights=='ALL' or raw_labels:
1109 weight_header = all_weight_header
1110 else:
1111 new_weight_header = []
1112
1113 for wgt_label in all_weight_header:
1114 if wgt_label in ['central','stat_error','boundary_xmin','boundary_xmax'] or\
1115 HwU.get_HwU_wgt_label_type(wgt_label) in consider_reweights:
1116 new_weight_header.append(wgt_label)
1117 weight_header = new_weight_header
1118
1119
1120 for line in stream:
1121 start = HwU.histo_start_re.match(line)
1122 if not start is None:
1123 self.process_histogram_name(start.group('histo_name'))
1124
1125
1126 if self.type == 'AUX':
1127 continue
1128 n_bins = int(start.group('n_bins'))
1129
1130
1131 self.bins = BinList(weight_labels = [ wgt_label for
1132 wgt_label in weight_header if wgt_label not in
1133 ['boundary_xmin','boundary_xmax']])
1134 break
1135
1136
1137 for line_bin in stream:
1138 bin_weights = {}
1139 boundaries = [0.0,0.0]
1140 for j, weight in \
1141 enumerate(HwU.histo_bin_weight_re.finditer(line_bin)):
1142 if j == len(all_weight_header):
1143 raise HwU.ParseError, "There is more bin weights"+\
1144 " specified than expected (%i)"%len(weight_header)
1145 if selected_central_weight == all_weight_header[j]:
1146 bin_weights['central'] = float(weight.group('weight'))
1147 if all_weight_header[j] == 'boundary_xmin':
1148 boundaries[0] = float(weight.group('weight'))
1149 elif all_weight_header[j] == 'boundary_xmax':
1150 boundaries[1] = float(weight.group('weight'))
1151 elif all_weight_header[j] == 'central' and not selected_central_weight is None:
1152 continue
1153 elif all_weight_header[j] in weight_header:
1154 bin_weights[all_weight_header[j]] = \
1155 float(weight.group('weight'))
1156
1157
1158
1159
1160 if len(bin_weights)<(len(weight_header)-2):
1161 raise HwU.ParseError, " There are only %i weights"\
1162 %len(bin_weights)+" specified and %i were expected."%\
1163 (len(weight_header)-2)
1164 self.bins.append(Bin(tuple(boundaries), bin_weights))
1165 if len(self.bins)==n_bins:
1166 break
1167
1168 if len(self.bins)!=n_bins:
1169 raise HwU.ParseError, "%i bin specification "%len(self.bins)+\
1170 "were found and %i were expected."%n_bins
1171
1172
1173 for line_end in stream:
1174 if HwU.histo_end_re.match(line_end):
1175
1176
1177 if not raw_labels:
1178 self.trim_auxiliary_weights()
1179
1180 return True
1181
1182
1183 return False
1184
1186 """ Remove all weights which are auxiliary (whose name end with '@aux')
1187 so that they are not included (they will be regenerated anyway)."""
1188
1189 for i, wgt_label in enumerate(self.bins.weight_labels):
1190 if isinstance(wgt_label, str) and wgt_label.endswith('@aux'):
1191 for bin in self.bins:
1192 try:
1193 del bin.wgts[wgt_label]
1194 except KeyError:
1195 pass
1196 self.bins.weight_labels = [wgt_label for wgt_label in
1197 self.bins.weight_labels if (not isinstance(wgt_label, str)
1198 or (isinstance(wgt_label, str) and not wgt_label.endswith('@aux')) )]
1199
1200 - def set_uncertainty(self, type='all_scale',lhapdfconfig='lhapdf-config'):
1201 """ Adds a weight to the bins which is the envelope of the scale
1202 uncertainty, for the scale specified which can be either 'mur', 'muf',
1203 'all_scale' or 'PDF'."""
1204
1205 if type.upper()=='MUR':
1206 new_wgt_label = 'delta_mur'
1207 scale_position = 1
1208 elif type.upper()=='MUF':
1209 new_wgt_label = 'delta_muf'
1210 scale_position = 2
1211 elif type.upper()=='ALL_SCALE':
1212 new_wgt_label = 'delta_mu'
1213 scale_position = -1
1214 elif type.upper()=='PDF':
1215 new_wgt_label = 'delta_pdf'
1216 scale_position = -2
1217 elif type.upper()=='MERGING':
1218 new_wgt_label = 'delta_merging'
1219 elif type.upper()=='ALPSFACT':
1220 new_wgt_label = 'delta_alpsfact'
1221 else:
1222 raise MadGraph5Error, ' The function set_uncertainty can'+\
1223 " only handle the scales 'mur', 'muf', 'all_scale', 'pdf',"+\
1224 "'merging' or 'alpsfact'."
1225
1226 wgts_to_consider=[]
1227 label_to_consider=[]
1228 if type.upper() == 'MERGING':
1229
1230
1231
1232 wgts_to_consider.append([ label for label in self.bins.weight_labels if \
1233 HwU.get_HwU_wgt_label_type(label)=='merging_scale' ])
1234 label_to_consider.append('none')
1235
1236 elif type.upper() == 'ALPSFACT':
1237
1238
1239
1240 wgts_to_consider.append([ label for label in self.bins.weight_labels if \
1241 HwU.get_HwU_wgt_label_type(label)=='alpsfact' ])
1242 label_to_consider.append('none')
1243 elif scale_position > -2:
1244
1245 dyn_scales=[label[1] for label in self.bins.weight_labels if \
1246 HwU.get_HwU_wgt_label_type(label)=='scale_adv']
1247
1248 dyn_scales=[scale for n,scale in enumerate(dyn_scales) if scale not in dyn_scales[:n]]
1249 for dyn_scale in dyn_scales:
1250 wgts=[label for label in self.bins.weight_labels if \
1251 HwU.get_HwU_wgt_label_type(label)=='scale_adv' and label[1]==dyn_scale]
1252 if wgts:
1253 wgts_to_consider.append(wgts)
1254 label_to_consider.append(dyn_scale)
1255
1256 wgts=[label for label in self.bins.weight_labels if \
1257 HwU.get_HwU_wgt_label_type(label)=='scale']
1258
1259
1260
1261
1262 if wgts:
1263 wgts_to_consider.append(wgts)
1264 label_to_consider.append('none')
1265
1266
1267 if scale_position > -1:
1268 for wgts in wgts_to_consider:
1269 wgts_to_consider.remove(wgts)
1270 wgts = [ label for label in wgts if label[-scale_position]==1.0 ]
1271 wgts_to_consider.append(wgts)
1272 elif scale_position == -2:
1273
1274 pdf_sets=[label[2] for label in self.bins.weight_labels if \
1275 HwU.get_HwU_wgt_label_type(label)=='pdf_adv']
1276
1277 pdf_sets=[ii for n,ii in enumerate(pdf_sets) if ii not in pdf_sets[:n]]
1278 for pdf_set in pdf_sets:
1279 wgts=[label for label in self.bins.weight_labels if \
1280 HwU.get_HwU_wgt_label_type(label)=='pdf_adv' and label[2]==pdf_set]
1281 if wgts:
1282 wgts_to_consider.append(wgts)
1283 label_to_consider.append(pdf_set)
1284
1285 wgts = [ label for label in self.bins.weight_labels if \
1286 HwU.get_HwU_wgt_label_type(label)=='pdf']
1287 if wgts:
1288 wgts_to_consider.append(wgts)
1289 label_to_consider.append('none')
1290
1291 if len(wgts_to_consider)==0 or all(len(wgts)==0 for wgts in wgts_to_consider):
1292
1293 return (None,[None])
1294
1295
1296 if type=='PDF':
1297 use_lhapdf=False
1298 try:
1299 lhapdf_libdir=subprocess.Popen([lhapdfconfig,'--libdir'],\
1300 stdout=subprocess.PIPE).stdout.read().strip()
1301 except:
1302 use_lhapdf=False
1303 else:
1304 try:
1305 candidates=[dirname for dirname in os.listdir(lhapdf_libdir) \
1306 if os.path.isdir(os.path.join(lhapdf_libdir,dirname))]
1307 except OSError:
1308 candidates=[]
1309 for candidate in candidates:
1310 if os.path.isfile(os.path.join(lhapdf_libdir,candidate,'site-packages','lhapdf.so')):
1311 sys.path.insert(0,os.path.join(lhapdf_libdir,candidate,'site-packages'))
1312 try:
1313 import lhapdf
1314 use_lhapdf=True
1315 break
1316 except ImportError:
1317 sys.path.pop(0)
1318 continue
1319
1320 if not use_lhapdf:
1321 try:
1322 candidates=[dirname for dirname in os.listdir(lhapdf_libdir+'64') \
1323 if os.path.isdir(os.path.join(lhapdf_libdir+'64',dirname))]
1324 except OSError:
1325 candidates=[]
1326 for candidate in candidates:
1327 if os.path.isfile(os.path.join(lhapdf_libdir+'64',candidate,'site-packages','lhapdf.so')):
1328 sys.path.insert(0,os.path.join(lhapdf_libdir+'64',candidate,'site-packages'))
1329 try:
1330 import lhapdf
1331 use_lhapdf=True
1332 break
1333 except ImportError:
1334 sys.path.pop(0)
1335 continue
1336
1337 if not use_lhapdf:
1338 try:
1339 import lhapdf
1340 use_lhapdf=True
1341 except ImportError:
1342 logger.warning("Failed to access python version of LHAPDF: "\
1343 "cannot compute PDF uncertainty from the "\
1344 "weights in the histograms. The weights in the HwU data files " \
1345 "still cover all PDF set members, "\
1346 "but the automatic computation of the uncertainties from "\
1347 "those weights might not be correct. \n "\
1348 "If the python interface to LHAPDF is available on your system, try "\
1349 "adding its location to the PYTHONPATH environment variable and the"\
1350 "LHAPDF library location to LD_LIBRARY_PATH (linux) or DYLD_LIBRARY_PATH (mac os x).")
1351
1352 if type=='PDF' and use_lhapdf:
1353 lhapdf.setVerbosity(0)
1354
1355
1356 position=[]
1357 labels=[]
1358 for i,label in enumerate(label_to_consider):
1359 wgts=wgts_to_consider[i]
1360 if label != 'none':
1361 new_wgt_labels=['%s_cen %s @aux' % (new_wgt_label,label),
1362 '%s_min %s @aux' % (new_wgt_label,label),
1363 '%s_max %s @aux' % (new_wgt_label,label)]
1364 else:
1365 new_wgt_labels=['%s_cen @aux' % new_wgt_label,
1366 '%s_min @aux' % new_wgt_label,
1367 '%s_max @aux' % new_wgt_label]
1368 try:
1369 pos=[(not isinstance(lab, str)) for lab in \
1370 self.bins.weight_labels].index(True)
1371 position.append(pos)
1372 labels.append(label)
1373 self.bins.weight_labels = self.bins.weight_labels[:pos]+\
1374 new_wgt_labels + self.bins.weight_labels[pos:]
1375 except ValueError:
1376 pos=len(self.bins.weight_labels)
1377 position.append(pos)
1378 labels.append(label)
1379 self.bins.weight_labels.extend(new_wgt_labels)
1380
1381 if type=='PDF' and use_lhapdf and label != 'none':
1382 p=lhapdf.getPDFSet(label)
1383
1384
1385 for bin in self.bins:
1386 if type!='PDF':
1387 bin.wgts[new_wgt_labels[0]] = bin.wgts[wgts[0]]
1388 bin.wgts[new_wgt_labels[1]] = min(bin.wgts[label] \
1389 for label in wgts)
1390 bin.wgts[new_wgt_labels[2]] = max(bin.wgts[label] \
1391 for label in wgts)
1392 elif type=='PDF' and use_lhapdf and label != 'none' and len(wgts) > 1:
1393 pdfs = [bin.wgts[pdf] for pdf in sorted(wgts)]
1394 ep=p.uncertainty(pdfs,-1)
1395 bin.wgts[new_wgt_labels[0]] = ep.central
1396 bin.wgts[new_wgt_labels[1]] = ep.central-ep.errminus
1397 bin.wgts[new_wgt_labels[2]] = ep.central+ep.errplus
1398 elif type=='PDF' and use_lhapdf and label != 'none' and len(bin.wgts) == 1:
1399 bin.wgts[new_wgt_labels[0]] = bin.wgts[wgts[0]]
1400 bin.wgts[new_wgt_labels[1]] = bin.wgts[wgts[0]]
1401 bin.wgts[new_wgt_labels[2]] = bin.wgts[wgts[0]]
1402 else:
1403 pdfs = [bin.wgts[pdf] for pdf in sorted(wgts)]
1404 pdf_up = 0.0
1405 pdf_down = 0.0
1406 cntrl_val = bin.wgts['central']
1407 if wgts[0] <= 90000:
1408
1409 if len(pdfs)>2:
1410 for i in range(int((len(pdfs)-1)/2)):
1411 pdf_up += max(0.0,pdfs[2*i+1]-cntrl_val,
1412 pdfs[2*i+2]-cntrl_val)**2
1413 pdf_down += max(0.0,cntrl_val-pdfs[2*i+1],
1414 cntrl_val-pdfs[2*i+2])**2
1415 pdf_up = cntrl_val + math.sqrt(pdf_up)
1416 pdf_down = cntrl_val - math.sqrt(pdf_down)
1417 else:
1418 pdf_up = bin.wgts[pdfs[0]]
1419 pdf_down = bin.wgts[pdfs[0]]
1420 elif wgts[0] in range(90200, 90303) or \
1421 wgts[0] in range(90400, 90433) or \
1422 wgts[0] in range(90700, 90801) or \
1423 wgts[0] in range(90900, 90931) or \
1424 wgts[0] in range(91200, 91303) or \
1425 wgts[0] in range(91400, 91433) or \
1426 wgts[0] in range(91700, 91801) or \
1427 wgts[0] in range(91900, 90931) or \
1428 wgts[0] in range(92000, 92031):
1429
1430 pdf_stdev = 0.0
1431 for pdf in pdfs[1:]:
1432 pdf_stdev += (pdf - cntrl_val)**2
1433 pdf_stdev = math.sqrt(pdf_stdev)
1434 pdf_up = cntrl_val+pdf_stdev
1435 pdf_down = cntrl_val-pdf_stdev
1436 elif wgts[0] in range(244400, 244501) or \
1437 wgts[0] in range(244600, 244701) or \
1438 wgts[0] in range(244800, 244901) or \
1439 wgts[0] in range(245000, 245101) or \
1440 wgts[0] in range(245200, 245301) or \
1441 wgts[0] in range(245400, 245501) or \
1442 wgts[0] in range(245600, 245701) or \
1443 wgts[0] in range(245800, 245901) or \
1444 wgts[0] in range(246000, 246101) or \
1445 wgts[0] in range(246200, 246301) or \
1446 wgts[0] in range(246400, 246501) or \
1447 wgts[0] in range(246600, 246701) or \
1448 wgts[0] in range(246800, 246901) or \
1449 wgts[0] in range(247000, 247101) or \
1450 wgts[0] in range(247200, 247301) or \
1451 wgts[0] in range(247400, 247501):
1452
1453 pdf_stdev = 0.0
1454 pdf_diff = sorted([abs(pdf-cntrl_val) for pdf in pdfs[1:]])
1455 pdf_stdev = pdf_diff[67]
1456 pdf_up = cntrl_val+pdf_stdev
1457 pdf_down = cntrl_val-pdf_stdev
1458 else:
1459
1460 pdf_stdev = 0.0
1461 for pdf in pdfs[1:]:
1462 pdf_stdev += (pdf - cntrl_val)**2
1463 pdf_stdev = math.sqrt(pdf_stdev/float(len(pdfs)-2))
1464 pdf_up = cntrl_val+pdf_stdev
1465 pdf_down = cntrl_val-pdf_stdev
1466
1467 bin.wgts[new_wgt_labels[0]] = bin.wgts[wgts[0]]
1468 bin.wgts[new_wgt_labels[1]] = pdf_down
1469 bin.wgts[new_wgt_labels[2]] = pdf_up
1470
1471
1472
1473 return (position,labels)
1474
1476 """ Select a specific merging scale for the central value of this Histogram. """
1477 if selected_label not in self.bins.weight_labels:
1478 raise MadGraph5Error, "Selected weight label '%s' could not be found in this HwU."%selected_label
1479
1480 for bin in self.bins:
1481 bin.wgts['central']=bin.wgts[selected_label]
1482
1483 - def rebin(self, n_rebin):
1484 """ Rebin the x-axis so as to merge n_rebin consecutive bins into a
1485 single one. """
1486
1487 if n_rebin < 1 or not isinstance(n_rebin, int):
1488 raise MadGraph5Error, "The argument 'n_rebin' of the HwU function"+\
1489 " 'rebin' must be larger or equal to 1, not '%s'."%str(n_rebin)
1490 elif n_rebin==1:
1491 return
1492
1493 if self.type and 'NOREBIN' in self.type.upper():
1494 return
1495
1496 rebinning_list = list(range(0,len(self.bins),n_rebin))+[len(self.bins),]
1497 concat_list = [self.bins[rebinning_list[i]:rebinning_list[i+1]] for \
1498 i in range(len(rebinning_list)-1)]
1499
1500 new_bins = copy.copy(self.bins)
1501 del new_bins[:]
1502
1503 for bins_to_merge in concat_list:
1504 if len(bins_to_merge)==0:
1505 continue
1506 new_bins.append(Bin(boundaries=(bins_to_merge[0].boundaries[0],
1507 bins_to_merge[-1].boundaries[1]),wgts={'central':0.0}))
1508 for weight in self.bins.weight_labels:
1509 if weight != 'stat_error':
1510 new_bins[-1].wgts[weight] = \
1511 sum(b.wgts[weight] for b in bins_to_merge)
1512 else:
1513 new_bins[-1].wgts['stat_error'] = \
1514 math.sqrt(sum(b.wgts['stat_error']**2 for b in\
1515 bins_to_merge))
1516
1517 self.bins = new_bins
1518
1519 @classmethod
1521 """ Function to determine the optimal x-axis range when plotting
1522 together the histos in histo_list and considering the weights
1523 weight_labels"""
1524
1525
1526 if weight_labels is None:
1527 weight_labels = histo_list[0].bins.weight_labels
1528
1529 all_boundaries = sum([ list(bin.boundaries) for histo in histo_list \
1530 for bin in histo.bins if \
1531 (sum(abs(bin.wgts[label]) for label in weight_labels) > 0.0)] ,[])
1532
1533 if len(all_boundaries)==0:
1534 all_boundaries = sum([ list(bin.boundaries) for histo in histo_list \
1535 for bin in histo.bins],[])
1536 if len(all_boundaries)==0:
1537 raise MadGraph5Error, "The histograms with title '%s'"\
1538 %histo_list[0].title+" seems to have no bins."
1539
1540 x_min = min(all_boundaries)
1541 x_max = max(all_boundaries)
1542
1543 return (x_min, x_max)
1544
1545 @classmethod
1548 """ Function to determine the optimal y-axis range when plotting
1549 together the histos in histo_list and considering the weights
1550 weight_labels. The option Kratio is present to allow for the couple of
1551 tweaks necessary for the the K-factor ratio histogram y-range."""
1552
1553
1554 if labels is None:
1555 weight_labels = histo_list[0].bins.weight_labels
1556 else:
1557 weight_labels = labels
1558
1559 all_weights = []
1560 for histo in histo_list:
1561 for bin in histo.bins:
1562 for label in weight_labels:
1563
1564
1565 if Kratio and bin.wgts[label]==0.0:
1566 continue
1567 if scale!='LOG':
1568 all_weights.append(bin.wgts[label])
1569 if label == 'stat_error':
1570 all_weights.append(-bin.wgts[label])
1571 elif bin.wgts[label]>0.0:
1572 all_weights.append(bin.wgts[label])
1573
1574
1575 sum([ [bin.wgts[label] for label in weight_labels if \
1576 (scale!='LOG' or bin.wgts[label]!=0.0)] \
1577 for histo in histo_list for bin in histo.bins], [])
1578
1579 all_weights.sort()
1580 if len(all_weights)!=0:
1581 partial_max = all_weights[int(len(all_weights)*0.95)]
1582 partial_min = all_weights[int(len(all_weights)*0.05)]
1583 max = all_weights[-1]
1584 min = all_weights[0]
1585 else:
1586 if scale!='LOG':
1587 return (0.0,1.0)
1588 else:
1589 return (1.0,10.0)
1590
1591 y_max = 0.0
1592 y_min = 0.0
1593
1594
1595 if (max-partial_max)>2.0*(partial_max-partial_min):
1596 y_max = partial_max
1597 else:
1598 y_max = max
1599
1600
1601 if (partial_min - min)>2.0*(partial_max-partial_min) and min != 0.0:
1602 y_min = partial_min
1603 else:
1604 y_min = min
1605
1606 if Kratio:
1607 median = all_weights[len(all_weights)//2]
1608 spread = (y_max-y_min)
1609 if abs(y_max-median)<spread*0.05 or abs(median-y_min)<spread*0.05:
1610 y_max = median + spread/2.0
1611 y_min = median - spread/2.0
1612 if y_min != y_max:
1613 return ( y_min , y_max )
1614
1615
1616 if len(histo_list[0].bins) <= 5:
1617 y_min = min
1618 y_max = max
1619
1620
1621 if y_min == y_max:
1622 if max == min:
1623 y_min -= 1.0
1624 y_max += 1.0
1625 else:
1626 y_min = min
1627 y_max = max
1628
1629 return ( y_min , y_max )
1630
1631 -class HwUList(histograms_PhysicsObjectList):
1632 """ A class implementing features related to a list of Hwu Histograms. """
1633
1634
1635
1636
1637 number_line_colors_defined = 8
1638
1640 """Test wether specified object is of the right type for this list."""
1641
1642 return isinstance(obj, HwU) or isinstance(obj, HwUList)
1643
1644 - def __init__(self, file_path, weight_header=None, run_id=None,
1645 merging_scale=None, accepted_types_order=[], consider_reweights='ALL',
1646 raw_labels=False, **opts):
1647 """ Read one plot from a file_path or a stream.
1648 This constructor reads all plots specified in target file.
1649 File_path can be a path or a stream in the argument.
1650 The option weight_header specifies an ordered list of weight names
1651 to appear in the file or stream specified. It accepted_types_order is
1652 empty, no filter is applied, otherwise only histograms of the specified
1653 types will be kept, and in this specified order for a given identical
1654 title. The option 'consider_reweights' selects whether one wants to
1655 include all the extra scale/pdf/merging variation weights. Possible values
1656 are 'ALL' or a list of the return types of the function get_HwU_wgt_label_type().
1657 The option 'raw_labels' specifies that one wants to import the
1658 histogram data with no treatment of the weight labels at all
1659 (this is used for the matplotlib output).
1660 """
1661
1662 if isinstance(file_path, str):
1663 stream = open(file_path,'r')
1664 elif isinstance(file_path, file):
1665 stream = file_path
1666 else:
1667 return super(HwUList,self).__init__(file_path, **opts)
1668
1669 try:
1670
1671 self.parse_histos_from_PY8_XML_stream(stream, run_id,
1672 merging_scale, accepted_types_order,
1673 consider_reweights=consider_reweights,
1674 raw_labels=raw_labels)
1675 except XMLParsingError:
1676
1677 stream.seek(0)
1678
1679 if not weight_header:
1680 weight_header = HwU.parse_weight_header(stream,raw_labels=raw_labels)
1681
1682
1683 selected_label = None
1684 if not merging_scale is None:
1685 for label in weight_header:
1686 if HwU.get_HwU_wgt_label_type(label)=='merging_scale':
1687 if float(label[1])==merging_scale:
1688 selected_label = label
1689 break
1690 if selected_label is None:
1691 raise MadGraph5Error, "No weight could be found in the input HwU "+\
1692 "for the selected merging scale '%4.2f'."%merging_scale
1693
1694 new_histo = HwU(stream, weight_header,raw_labels=raw_labels,
1695 consider_reweights=consider_reweights,
1696 selected_central_weight=selected_label)
1697
1698 while not new_histo.bins is None:
1699 if accepted_types_order==[] or \
1700 new_histo.type in accepted_types_order:
1701 self.append(new_histo)
1702 new_histo = HwU(stream, weight_header, raw_labels=raw_labels,
1703 consider_reweights=consider_reweights,
1704 selected_central_weight=selected_label)
1705
1706
1707
1708
1709
1710
1711
1712 titles_order = [h.title for h in self]
1713 def ordering_function(histo):
1714 title_position = titles_order.index(histo.title)
1715 if accepted_types_order==[]:
1716 type_precedence = {'NLO':1,'LO':2,None:3,'AUX':5}
1717 try:
1718 ordering_key = (title_position,type_precedence[histo.type])
1719 except KeyError:
1720 ordering_key = (title_position,4)
1721 else:
1722 ordering_key = (title_position,
1723 accepted_types_order.index(histo.type))
1724 return ordering_key
1725
1726
1727
1728
1729
1730 self.sort(key=ordering_function)
1731
1732
1733 if isinstance(file_path, str):
1734 stream.close()
1735
1743
1745 """ return the list of all weights define in each histograms"""
1746
1747 return self[0].bins.weight_labels
1748
1749
1750 - def get(self, name):
1751 """return the HWU histograms related to a given name"""
1752 for hist in self:
1753 if hist.get_HwU_histogram_name() == name:
1754 return hist
1755
1756 raise NameError, "no histogram with name: %s" % name
1757
1758 - def parse_histos_from_PY8_XML_stream(self, stream, run_id=None,
1759 merging_scale=None, accepted_types_order=[],
1760 consider_reweights='ALL', raw_labels=False):
1761 """Initialize the HwU histograms from an XML stream. Only one run is
1762 used: the first one if run_id is None or the specified run otherwise.
1763 Accepted type order is a filter to select histograms of only a certain
1764 type. The option 'consider_reweights' selects whether one wants to
1765 include all the extra scale/pdf/merging variation weights.
1766 Possible values are 'ALL' or a list of the return types of the
1767 function get_HwU_wgt_label_type()."""
1768
1769 run_nodes = minidom.parse(stream).getElementsByTagName("run")
1770 all_nodes = dict((int(node.getAttribute('id')),node) for
1771 node in run_nodes)
1772 selected_run_node = None
1773 weight_header = None
1774 if run_id is None:
1775 if len(run_nodes)>0:
1776 selected_run_node = all_nodes[min(all_nodes.keys())]
1777 else:
1778 try:
1779 selected_run_node = all_nodes[int(run_id)]
1780 except:
1781 selected_run_node = None
1782
1783 if selected_run_node is None:
1784 if run_id is None:
1785 raise MadGraph5Error, \
1786 'No histogram was found in the specified XML source.'
1787 else:
1788 raise MadGraph5Error, \
1789 "Histogram with run_id '%d' was not found in the "%run_id+\
1790 "specified XML source."
1791
1792
1793
1794 if raw_labels:
1795
1796 weight_label_list = [wgt.strip() for wgt in
1797 str(selected_run_node.getAttribute('header')).split(';') if
1798 not re.match('^\s*$',wgt)]
1799 ordered_weight_label_list = [w for w in weight_label_list if w not\
1800 in ['xmin','xmax']]
1801
1802 filtered_ordered_weight_label_list = []
1803 for wgt_label in ordered_weight_label_list:
1804 if wgt_label not in filtered_ordered_weight_label_list:
1805 filtered_ordered_weight_label_list.append(wgt_label)
1806
1807 selected_weights = dict([ (wgt_pos,
1808 [wgt if wgt not in ['xmin','xmax'] else HwU.mandatory_weights[wgt]])
1809 for wgt_pos, wgt in enumerate(weight_label_list) if wgt in
1810 filtered_ordered_weight_label_list+['xmin','xmax']])
1811
1812 return self.retrieve_plots_from_XML_source(selected_run_node,
1813 selected_weights, filtered_ordered_weight_label_list,
1814 raw_labels=True)
1815
1816
1817
1818
1819 all_weights = []
1820 for wgt_position, wgt_label in \
1821 enumerate(str(selected_run_node.getAttribute('header')).split(';')):
1822 if not re.match('^\s*$',wgt_label) is None:
1823 continue
1824 all_weights.append({'POSITION':wgt_position})
1825 for wgt_item in wgt_label.strip().split('_'):
1826 property = wgt_item.strip().split('=')
1827 if len(property) == 2:
1828 all_weights[-1][property[0].strip()] = property[1].strip()
1829 elif len(property)==1:
1830 all_weights[-1][property[0].strip()] = None
1831 else:
1832 raise MadGraph5Error, \
1833 "The weight label property %s could not be parsed."%wgt_item
1834
1835
1836
1837
1838
1839 for wgt_label in all_weights:
1840 for mandatory_attribute in ['PDF','MUR','MUF','MERGING','ALPSFACT']:
1841 if mandatory_attribute not in wgt_label:
1842 wgt_label[mandatory_attribute] = '-1'
1843 if mandatory_attribute=='PDF':
1844 wgt_label[mandatory_attribute] = int(wgt_label[mandatory_attribute])
1845 elif mandatory_attribute in ['MUR','MUF','MERGING','ALPSFACT']:
1846 wgt_label[mandatory_attribute] = float(wgt_label[mandatory_attribute])
1847
1848
1849
1850
1851 if merging_scale is None or merging_scale < 0.0:
1852 merging_scale_chosen = all_weights[2]['MERGING']
1853 else:
1854 merging_scale_chosen = merging_scale
1855
1856
1857 central_PDF = all_weights[2]['PDF']
1858
1859 central_MUR = all_weights[2]['MUR'] if all_weights[2]['MUR']!=-1.0 else 1.0
1860 central_MUF = all_weights[2]['MUF'] if all_weights[2]['MUF']!=-1.0 else 1.0
1861 central_alpsfact = all_weights[2]['ALPSFACT'] if all_weights[2]['ALPSFACT']!=-1.0 else 1.0
1862
1863
1864
1865 selected_weights = {}
1866
1867 if 'xmin' not in all_weights[0] or \
1868 'xmax' not in all_weights[1] or \
1869 'Weight' not in all_weights[2] or \
1870 'WeightError' not in all_weights[3]:
1871 raise MadGraph5Error, 'The first weight entries in the XML HwU '+\
1872 ' source are not the standard expected ones (xmin, xmax, sigmaCentral, errorCentral)'
1873 selected_weights[0] = ['xmin']
1874 selected_weights[1] = ['xmax']
1875
1876
1877 def get_difference_to_central(weight):
1878 """ Return the list of properties which differ from the central weight.
1879 This disregards the merging scale value for which any central value
1880 can be picked anyway."""
1881
1882 differences = []
1883
1884
1885
1886 if 'Weight' in weight:
1887 return set([])
1888 if weight['MUR'] not in [central_MUR, -1.0] or \
1889 weight['MUF'] not in [central_MUF, -1.0]:
1890 differences.append('mur_muf_scale')
1891 if weight['PDF'] not in [central_PDF,-1]:
1892 differences.append('pdf')
1893 if weight['ALPSFACT'] not in [central_alpsfact, -1]:
1894 differences.append('ALPSFACT')
1895 return set(differences)
1896
1897 def format_weight_label(weight):
1898 """ Print the weight attributes in a nice order."""
1899
1900 all_properties = weight.keys()
1901 all_properties.pop(all_properties.index('POSITION'))
1902 ordered_properties = []
1903
1904 for property in all_properties:
1905 if weight[property] is None:
1906 ordered_properties.append(property)
1907
1908 ordered_properties.sort()
1909 all_properties = [property for property in all_properties if
1910 not weight[property] is None]
1911
1912
1913 for property in ['PDF','MUR','MUF','ALPSFACT','MERGING']:
1914 all_properties.pop(all_properties.index(property))
1915 if weight[property]!=-1:
1916 ordered_properties.append(property)
1917
1918 ordered_properties.extend(sorted(all_properties))
1919
1920 return '_'.join('%s%s'\
1921 %(key,'' if weight[key] is None else '=%s'%str(weight[key])) for
1922 key in ordered_properties)
1923
1924
1925
1926
1927
1928 if float(all_weights[2]['MERGING']) == merging_scale_chosen:
1929 selected_weights[2]=['central value']
1930 else:
1931 for weight_position, weight in enumerate(all_weights):
1932
1933
1934 if get_difference_to_central(weight)==set([]):
1935
1936 if weight['MERGING']==merging_scale_chosen:
1937 selected_weights[weight_position] = ['central value']
1938 break
1939
1940 if 'central value' not in sum(selected_weights.values(),[]):
1941 central_merging_scale = all_weights[2]['MERGING']
1942 logger.warning('Could not find the central weight for the'+\
1943 ' chosen merging scale (%f).\n'%merging_scale_chosen+\
1944 'MG5aMC will chose the original central scale provided which '+\
1945 'correspond to a merging scale of %s'%("'inclusive'" if
1946 central_merging_scale in [0.0,-1.0] else '%f'%central_merging_scale))
1947 selected_weights[2]=['central value']
1948
1949
1950 selected_weights[3]=['dy']
1951
1952
1953 for weight_position, weight in enumerate(all_weights[4:]):
1954
1955
1956
1957
1958
1959
1960 variations = get_difference_to_central(weight)
1961
1962
1963
1964
1965
1966
1967
1968 if variations in [set(['mur_muf_scale']),set(['pdf','mur_muf_scale'])]:
1969 wgt_label = ('scale',weight['MUR'],weight['MUF'])
1970 if variations in [set(['ALPSFACT']),set(['pdf','ALPSFACT'])]:
1971 wgt_label = ('alpsfact',weight['ALPSFACT'])
1972 if variations == set(['pdf']):
1973 wgt_label = ('pdf',weight['PDF'])
1974 if variations == set([]):
1975
1976 wgt_label = format_weight_label(weight)
1977
1978
1979 if weight['MERGING'] != merging_scale_chosen:
1980
1981 if merging_scale:
1982 continue
1983
1984
1985 if variations == set([]):
1986
1987 wgt_label = ('merging_scale', weight['MERGING'])
1988
1989
1990
1991 if wgt_label in sum(selected_weights.values(),[]):
1992 continue
1993
1994
1995 try:
1996 selected_weights[weight_position+4].append(wgt_label)
1997 except KeyError:
1998 selected_weights[weight_position+4]=[wgt_label,]
1999
2000 if merging_scale and merging_scale > 0.0 and \
2001 len(sum(selected_weights.values(),[]))==4:
2002 logger.warning('No additional variation weight was found for the '+\
2003 'chosen merging scale %f.'%merging_scale)
2004
2005
2006 for wgt_pos in selected_weights:
2007 for i, weight_label in enumerate(selected_weights[wgt_pos]):
2008 try:
2009 selected_weights[wgt_pos][i] = HwU.mandatory_weights[weight_label]
2010 except KeyError:
2011 pass
2012
2013
2014 if consider_reweights!='ALL':
2015 new_selected_weights = {}
2016 for wgt_position, wgt_labels in selected_weights.items():
2017 for wgt_label in wgt_labels:
2018 if wgt_label in ['central','stat_error','boundary_xmin','boundary_xmax'] or\
2019 HwU.get_HwU_wgt_label_type(wgt_label) in consider_reweights:
2020 try:
2021 new_selected_weights[wgt_position].append(wgt_label)
2022 except KeyError:
2023 new_selected_weights[wgt_position] = [wgt_label]
2024 selected_weights = new_selected_weights
2025
2026
2027 weight_label_list = sum(selected_weights.values(),[])
2028
2029
2030 ordered_weight_label_list = ['central','stat_error']
2031 for weight_label in weight_label_list:
2032 if not isinstance(weight_label, str):
2033 ordered_weight_label_list.append(weight_label)
2034 for weight_label in weight_label_list:
2035 if weight_label in ['central','stat_error','boundary_xmin','boundary_xmax']:
2036 continue
2037 if isinstance(weight_label, str):
2038 ordered_weight_label_list.append(weight_label)
2039
2040
2041
2042 return self.retrieve_plots_from_XML_source(selected_run_node,
2043 selected_weights, ordered_weight_label_list, raw_labels=False)
2044
2047 """Given an XML node and the selected weights and their ordered list,
2048 import all histograms from the specified XML node."""
2049
2050
2051 for multiplicity_node in xml_node.getElementsByTagName("jethistograms"):
2052 multiplicity = int(multiplicity_node.getAttribute('njet'))
2053 for histogram in multiplicity_node.getElementsByTagName("histogram"):
2054
2055 if histogram.getAttribute("weight")!='all':
2056 continue
2057 new_histo = HwU()
2058 hist_name = '%s %s'%(str(histogram.getAttribute('name')),
2059 str(histogram.getAttribute('unit')))
2060
2061 new_histo.process_histogram_name('%s |JETSAMPLE@%d'%(hist_name,multiplicity))
2062
2063
2064 if new_histo.type == 'AUX':
2065 continue
2066
2067
2068
2069 new_histo.bins = BinList(weight_labels = ordered_weight_label_list)
2070 hist_data = str(histogram.childNodes[0].data)
2071 for line in hist_data.split('\n'):
2072 if line.strip()=='':
2073 continue
2074 bin_weights = {}
2075 boundaries = [0.0,0.0]
2076 for j, weight in \
2077 enumerate(HwU.histo_bin_weight_re.finditer(line)):
2078 try:
2079 for wgt_label in selected_weights[j]:
2080 if wgt_label == 'boundary_xmin':
2081 boundaries[0] = float(weight.group('weight'))
2082 elif wgt_label == 'boundary_xmax':
2083 boundaries[1] = float(weight.group('weight'))
2084 else:
2085 if weight.group('weight').upper()=='NAN':
2086 raise MadGraph5Error, \
2087 "Some weights are found to be 'NAN' in histogram with name '%s'"%hist_name+\
2088 " and jet sample multiplicity %d."%multiplicity
2089 else:
2090 bin_weights[wgt_label] = \
2091 float(weight.group('weight'))
2092 except KeyError:
2093 continue
2094
2095 if len(bin_weights)!=len(ordered_weight_label_list):
2096 raise MadGraph5Error, \
2097 'Not all defined weights were found in the XML source.\n'+\
2098 '%d found / %d expected.'%(len(bin_weights),len(ordered_weight_label_list))+\
2099 '\nThe missing ones are: %s.'%\
2100 str(list(set(ordered_weight_label_list)-set(bin_weights.keys())))+\
2101 "\nIn plot with title '%s' and jet sample multiplicity %d."%\
2102 (hist_name, multiplicity)
2103
2104 new_histo.bins.append(Bin(tuple(boundaries), bin_weights))
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122 if not raw_labels:
2123 new_histo.trim_auxiliary_weights()
2124
2125
2126 self.append(new_histo)
2127
2128 - def output(self, path, format='gnuplot',number_of_ratios = -1,
2129 uncertainties=['scale','pdf','statitistical','merging_scale','alpsfact'],
2130 use_band = None,
2131 ratio_correlations=True, arg_string='',
2132 jet_samples_to_keep=None,
2133 auto_open=True,
2134 lhapdfconfig='lhapdf-config'):
2135 """ Ouput this histogram to a file, stream or string if path is kept to
2136 None. The supported format are for now. Chose whether to print the header
2137 or not."""
2138
2139 if len(self)==0:
2140 return MadGraph5Error, 'No histograms stored in the list yet.'
2141
2142 if not format in HwU.output_formats_implemented:
2143 raise MadGraph5Error, "The specified output format '%s'"%format+\
2144 " is not yet supported. Supported formats are %s."\
2145 %HwU.output_formats_implemented
2146
2147 if isinstance(path, str) and not any(ext in os.path.basename(path) \
2148 for ext in ['.Hwu','.ps','.gnuplot','.pdf']):
2149 output_base_name = os.path.basename(path)
2150 HwU_stream = open(path+'.HwU','w')
2151 else:
2152 raise MadGraph5Error, "The path argument of the output function of"+\
2153 " the HwUList instance must be file path without its extension."
2154
2155 HwU_output_list = []
2156
2157
2158 if format == 'HwU':
2159 HwU_output_list.extend(self[0].get_HwU_source(print_header=True))
2160 for histo in self[1:]:
2161 HwU_output_list.extend(histo.get_HwU_source())
2162 HwU_output_list.extend(['',''])
2163 HwU_stream.write('\n'.join(HwU_output_list))
2164 HwU_stream.close()
2165 return
2166
2167
2168 if format == 'gnuplot':
2169 gnuplot_stream = open(path+'.gnuplot','w')
2170
2171
2172 matching_histo_lists = HwUList([HwUList([self[0]])])
2173 for histo in self[1:]:
2174 matched = False
2175 for histo_list in matching_histo_lists:
2176 if histo.test_plot_compability(histo_list[0],
2177 consider_type=False, consider_unknown_weight_labels=True):
2178 histo_list.append(histo)
2179 matched = True
2180 break
2181 if not matched:
2182 matching_histo_lists.append(HwUList([histo]))
2183
2184 self[:] = matching_histo_lists
2185
2186
2187 gnuplot_output_list_v4 = [
2188 """
2189 ################################################################################
2190 #
2191 # This gnuplot file was generated by MadGraph5_aMC@NLO project, a program which
2192 # automatically generates Feynman diagrams and matrix elements for arbitrary
2193 # high-energy processes in the Standard Model and beyond. It also perform the
2194 # integration and/or generate events for these processes, at LO and NLO accuracy.
2195 #
2196 # For more information, visit madgraph.phys.ucl.ac.be and amcatnlo.web.cern.ch
2197 #
2198 ################################################################################
2199 # %s
2200 reset
2201
2202 set lmargin 10
2203 set rmargin 0
2204 set terminal postscript portrait enhanced mono dashed lw 1.0 "Helvetica" 9
2205 # The pdf terminal offers transparency support, but you will have to adapt things a bit
2206 #set terminal pdf enhanced font "Helvetica 12" lw 1.0 dashed size 29.7cm, 21cm
2207 set key font ",9"
2208 set key samplen "2"
2209 set output "%s.ps"
2210
2211 # This is the "PODO" color palette of gnuplot v.5, but with the order
2212 # changed: palette of colors selected to be easily distinguishable by
2213 # color-blind individuals with either protanopia or deuteranopia. Bang
2214 # Wong [2011] Nature Methods 8, 441.
2215
2216 set style line 1 lt 1 lc rgb "#009e73" lw 2.5
2217 set style line 11 lt 2 lc rgb "#009e73" lw 2.5
2218 set style line 21 lt 4 lc rgb "#009e73" lw 2.5
2219 set style line 31 lt 6 lc rgb "#009e73" lw 2.5
2220 set style line 41 lt 8 lc rgb "#009e73" lw 2.5
2221
2222 set style line 2 lt 1 lc rgb "#0072b2" lw 2.5
2223 set style line 12 lt 2 lc rgb "#0072b2" lw 2.5
2224 set style line 22 lt 4 lc rgb "#0072b2" lw 2.5
2225 set style line 32 lt 6 lc rgb "#0072b2" lw 2.5
2226 set style line 42 lt 8 lc rgb "#0072b2" lw 2.5
2227
2228 set style line 3 lt 1 lc rgb "#d55e00" lw 2.5
2229 set style line 13 lt 2 lc rgb "#d55e00" lw 2.5
2230 set style line 23 lt 4 lc rgb "#d55e00" lw 2.5
2231 set style line 33 lt 6 lc rgb "#d55e00" lw 2.5
2232 set style line 43 lt 8 lc rgb "#d55e00" lw 2.5
2233
2234 set style line 4 lt 1 lc rgb "#f0e442" lw 2.5
2235 set style line 14 lt 2 lc rgb "#f0e442" lw 2.5
2236 set style line 24 lt 4 lc rgb "#f0e442" lw 2.5
2237 set style line 34 lt 6 lc rgb "#f0e442" lw 2.5
2238 set style line 44 lt 8 lc rgb "#f0e442" lw 2.5
2239
2240 set style line 5 lt 1 lc rgb "#56b4e9" lw 2.5
2241 set style line 15 lt 2 lc rgb "#56b4e9" lw 2.5
2242 set style line 25 lt 4 lc rgb "#56b4e9" lw 2.5
2243 set style line 35 lt 6 lc rgb "#56b4e9" lw 2.5
2244 set style line 45 lt 8 lc rgb "#56b4e9" lw 2.5
2245
2246 set style line 6 lt 1 lc rgb "#cc79a7" lw 2.5
2247 set style line 16 lt 2 lc rgb "#cc79a7" lw 2.5
2248 set style line 26 lt 4 lc rgb "#cc79a7" lw 2.5
2249 set style line 36 lt 6 lc rgb "#cc79a7" lw 2.5
2250 set style line 46 lt 8 lc rgb "#cc79a7" lw 2.5
2251
2252 set style line 7 lt 1 lc rgb "#e69f00" lw 2.5
2253 set style line 17 lt 2 lc rgb "#e69f00" lw 2.5
2254 set style line 27 lt 4 lc rgb "#e69f00" lw 2.5
2255 set style line 37 lt 6 lc rgb "#e69f00" lw 2.5
2256 set style line 47 lt 8 lc rgb "#e69f00" lw 2.5
2257
2258 set style line 8 lt 1 lc rgb "black" lw 2.5
2259 set style line 18 lt 2 lc rgb "black" lw 2.5
2260 set style line 28 lt 4 lc rgb "black" lw 2.5
2261 set style line 38 lt 6 lc rgb "black" lw 2.5
2262 set style line 48 lt 7 lc rgb "black" lw 2.5
2263
2264
2265 set style line 999 lt 1 lc rgb "gray" lw 2.5
2266
2267 safe(x,y,a) = (y == 0.0 ? a : x/y)
2268
2269 set style data histeps
2270 set key invert
2271
2272 """%(arg_string,output_base_name)
2273 ]
2274
2275 gnuplot_output_list_v5 = [
2276 """
2277 ################################################################################
2278 #
2279 # This gnuplot file was generated by MadGraph5_aMC@NLO project, a program which
2280 # automatically generates Feynman diagrams and matrix elements for arbitrary
2281 # high-energy processes in the Standard Model and beyond. It also perform the
2282 # integration and/or generate events for these processes, at LO and NLO accuracy.
2283 #
2284 # For more information, visit madgraph.phys.ucl.ac.be and amcatnlo.web.cern.ch
2285 #
2286 ################################################################################
2287 # %s
2288 reset
2289
2290 set lmargin 10
2291 set rmargin 0
2292 set terminal postscript portrait enhanced color "Helvetica" 9
2293 # The pdf terminal offers transparency support, but you will have to adapt things a bit
2294 #set terminal pdf enhanced font "Helvetica 12" lw 1.0 dashed size 29.7cm, 21cm
2295 set key font ",9"
2296 set key samplen "2"
2297 set output "%s.ps"
2298
2299 # This is the "PODO" color palette of gnuplot v.5, but with the order
2300 # changed: palette of colors selected to be easily distinguishable by
2301 # color-blind individuals with either protanopia or deuteranopia. Bang
2302 # Wong [2011] Nature Methods 8, 441.
2303
2304 set style line 1 lt 1 lc rgb "#009e73" lw 1.3
2305 set style line 101 lt 1 lc rgb "#009e73" lw 1.3 dt (6,3)
2306 set style line 11 lt 2 lc rgb "#009e73" lw 1.3 dt (6,3)
2307 set style line 21 lt 4 lc rgb "#009e73" lw 1.3 dt (3,2)
2308 set style line 31 lt 6 lc rgb "#009e73" lw 1.3 dt (2,1)
2309 set style line 41 lt 8 lc rgb "#009e73" lw 1.3 dt (4,3)
2310
2311 set style line 2 lt 1 lc rgb "#0072b2" lw 1.3
2312 set style line 102 lt 1 lc rgb "#0072b2" lw 1.3 dt (6,3)
2313 set style line 12 lt 2 lc rgb "#0072b2" lw 1.3 dt (6,3)
2314 set style line 22 lt 4 lc rgb "#0072b2" lw 1.3 dt (3,2)
2315 set style line 32 lt 6 lc rgb "#0072b2" lw 1.3 dt (2,1)
2316 set style line 42 lt 8 lc rgb "#0072b2" lw 1.3 dt (4,3)
2317
2318
2319 set style line 3 lt 1 lc rgb "#d55e00" lw 1.3
2320 set style line 103 lt 1 lc rgb "#d55e00" lw 1.3 dt (6,3)
2321 set style line 13 lt 2 lc rgb "#d55e00" lw 1.3 dt (6,3)
2322 set style line 23 lt 4 lc rgb "#d55e00" lw 1.3 dt (3,2)
2323 set style line 33 lt 6 lc rgb "#d55e00" lw 1.3 dt (2,1)
2324 set style line 43 lt 8 lc rgb "#d55e00" lw 1.3 dt (4,3)
2325
2326 set style line 4 lt 1 lc rgb "#f0e442" lw 1.3
2327 set style line 104 lt 1 lc rgb "#f0e442" lw 1.3 dt (6,3)
2328 set style line 14 lt 2 lc rgb "#f0e442" lw 1.3 dt (6,3)
2329 set style line 24 lt 4 lc rgb "#f0e442" lw 1.3 dt (3,2)
2330 set style line 34 lt 6 lc rgb "#f0e442" lw 1.3 dt (2,1)
2331 set style line 44 lt 8 lc rgb "#f0e442" lw 1.3 dt (4,3)
2332
2333 set style line 5 lt 1 lc rgb "#56b4e9" lw 1.3
2334 set style line 105 lt 1 lc rgb "#56b4e9" lw 1.3 dt (6,3)
2335 set style line 15 lt 2 lc rgb "#56b4e9" lw 1.3 dt (6,3)
2336 set style line 25 lt 4 lc rgb "#56b4e9" lw 1.3 dt (3,2)
2337 set style line 35 lt 6 lc rgb "#56b4e9" lw 1.3 dt (2,1)
2338 set style line 45 lt 8 lc rgb "#56b4e9" lw 1.3 dt (4,3)
2339
2340 set style line 6 lt 1 lc rgb "#cc79a7" lw 1.3
2341 set style line 106 lt 1 lc rgb "#cc79a7" lw 1.3 dt (6,3)
2342 set style line 16 lt 2 lc rgb "#cc79a7" lw 1.3 dt (6,3)
2343 set style line 26 lt 4 lc rgb "#cc79a7" lw 1.3 dt (3,2)
2344 set style line 36 lt 6 lc rgb "#cc79a7" lw 1.3 dt (2,1)
2345 set style line 46 lt 8 lc rgb "#cc79a7" lw 1.3 dt (4,3)
2346
2347 set style line 7 lt 1 lc rgb "#e69f00" lw 1.3
2348 set style line 107 lt 1 lc rgb "#e69f00" lw 1.3 dt (6,3)
2349 set style line 17 lt 2 lc rgb "#e69f00" lw 1.3 dt (6,3)
2350 set style line 27 lt 4 lc rgb "#e69f00" lw 1.3 dt (3,2)
2351 set style line 37 lt 6 lc rgb "#e69f00" lw 1.3 dt (2,1)
2352 set style line 47 lt 8 lc rgb "#e69f00" lw 1.3 dt (4,3)
2353
2354 set style line 8 lt 1 lc rgb "black" lw 1.3
2355 set style line 108 lt 1 lc rgb "black" lw 1.3 dt (6,3)
2356 set style line 18 lt 2 lc rgb "black" lw 1.3 dt (6,3)
2357 set style line 28 lt 4 lc rgb "black" lw 1.3 dt (3,2)
2358 set style line 38 lt 6 lc rgb "black" lw 1.3 dt (2,1)
2359 set style line 48 lt 8 lc rgb "black" lw 1.3 dt (4,3)
2360
2361
2362 set style line 999 lt 1 lc rgb "gray" lw 1.3
2363
2364 safe(x,y,a) = (y == 0.0 ? a : x/y)
2365
2366 set style data histeps
2367 set key invert
2368
2369 """%(arg_string,output_base_name)
2370 ]
2371
2372
2373 try:
2374 p = subprocess.Popen(['gnuplot', '--version'], \
2375 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
2376 except OSError:
2377
2378
2379 gnuplot_output_list=gnuplot_output_list_v5
2380 else:
2381 output, _ = p.communicate()
2382 if float(output.split()[1]) < 5. :
2383 gnuplot_output_list=gnuplot_output_list_v4
2384 else:
2385 gnuplot_output_list=gnuplot_output_list_v5
2386
2387
2388
2389
2390 block_position = 0
2391 for histo_group in self:
2392
2393 block_position = histo_group.output_group(HwU_output_list,
2394 gnuplot_output_list, block_position,output_base_name+'.HwU',
2395 number_of_ratios=number_of_ratios,
2396 uncertainties = uncertainties,
2397 use_band = use_band,
2398 ratio_correlations = ratio_correlations,
2399 jet_samples_to_keep=jet_samples_to_keep,
2400 lhapdfconfig = lhapdfconfig)
2401
2402
2403 gnuplot_output_list.extend([
2404 "unset multiplot",
2405 '!ps2pdf "%s.ps" &> /dev/null'%output_base_name])
2406 if auto_open:
2407 gnuplot_output_list.append(
2408 '!open "%s.pdf" &> /dev/null'%output_base_name)
2409
2410
2411 gnuplot_stream.write('\n'.join(gnuplot_output_list))
2412 HwU_stream.write('\n'.join(HwU_output_list))
2413 gnuplot_stream.close()
2414 HwU_stream.close()
2415
2416 logger.debug("Histograms have been written out at "+\
2417 "%s.[HwU|gnuplot]' and can "%output_base_name+\
2418 "now be rendered by invoking gnuplot.")
2419
2420 - def output_group(self, HwU_out, gnuplot_out, block_position, HwU_name,
2421 number_of_ratios = -1,
2422 uncertainties = ['scale','pdf','statitistical','merging_scale','alpsfact'],
2423 use_band = None,
2424 ratio_correlations = True,
2425 jet_samples_to_keep=None,
2426 lhapdfconfig='lhapdf-config'):
2427
2428 """ This functions output a single group of histograms with either one
2429 histograms untyped (i.e. type=None) or two of type 'NLO' and 'LO'
2430 respectively."""
2431
2432
2433
2434 def get_main_central_plot_lines(HwU_name, block_position, color_index,
2435 title, show_mc_uncertainties):
2436 """ Returns two plot lines, one for the negative contributions in
2437 dashed and one with the positive ones in solid."""
2438
2439 template = "'%(hwu)s' index %(ind)d using (($1+$2)/2):%(data)s%(stat_col)s%(stat_err)s%(ls)s%(title)s"
2440 template_no_stat = "'%(hwu)s' index %(ind)d using (($1+$2)/2):%(data)s%(ls)s%(title)s"
2441 rep_dic = {'hwu':HwU_name,
2442 'ind':block_position,
2443 'ls':' ls %d'%color_index,
2444 'title':" title '%s'"%title,
2445 'stat_col': ':4',
2446 'stat_err': ' w yerrorbar',
2447 'data':'3',
2448 'linetype':''}
2449
2450
2451
2452
2453
2454
2455 res = []
2456 rep_dic['data'] = '($3 < 0 ? sqrt(-1) : $3)'
2457 res.append(template_no_stat%rep_dic)
2458 rep_dic['title'] = " title ''"
2459 if show_mc_uncertainties:
2460 res.append(template%rep_dic)
2461 rep_dic['data'] = '($3 >= 0 ? sqrt(-1) : abs($3))'
2462 rep_dic['ls'] = ' ls %d'%(100+color_index)
2463 res.append(template_no_stat%rep_dic)
2464 if show_mc_uncertainties:
2465 res.append(template%rep_dic)
2466 return res
2467
2468
2469
2470
2471 def get_uncertainty_lines(HwU_name, block_position,
2472 var_pos, color_index,title, ratio=False, band=False):
2473 """ Return a string line corresponding to the plotting of the
2474 uncertainty. Band is to chose wether to display uncertainty with
2475 a band or two lines."""
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495 copy_swap_re = r"perl -pe 's/^\s*(?<x1>[\+|-]?\d+(\.\d*)?([EeDd][\+|-]?\d+)?)\s*(?<x2>[\+|-]?\d+(\.\d*)?([EeDd][\+|-]?\d+)?)(?<rest>.*)\n/ $+{x1} $+{x2} $+{rest}\n$+{x2} $+{x1} $+{rest}\n/g'"
2496
2497
2498 copy_swap_re = copy_swap_re.replace('\\','\\\\')
2499
2500 position = '(safe($%d,$3,1.0)-1.0)' if ratio else '%d'
2501 if not band:
2502 return ["'%s' index %d using (($1+$2)/2):%s ls %d title '%s'"\
2503 %(HwU_name,block_position, position%(var_pos),color_index,title),
2504 "'%s' index %d using (($1+$2)/2):%s ls %d title ''"\
2505 %(HwU_name,block_position, position%(var_pos+1),color_index)]
2506 else:
2507 return [' "<%s %s" index %d using 1:%s:%s with filledcurve ls %d fs transparent solid 0.2 title \'%s\''%\
2508 (copy_swap_re,HwU_name,block_position,
2509 position%var_pos,position%(var_pos+1),color_index,title)]
2510
2511
2512
2513 layout_geometry = [(0.0, 0.5, 1.0, 0.4 ),
2514 (0.0, 0.35, 1.0, 0.15),
2515 (0.0, 0.2, 1.0, 0.15)]
2516 layout_geometry.reverse()
2517
2518
2519
2520 matching_histo_lists = HwUList([HwUList([self[0]])])
2521 for histo in self[1:]:
2522 matched = False
2523 for histo_list in matching_histo_lists:
2524 if hasattr(histo, 'jetsample') and histo.jetsample >= 0 and \
2525 histo.type == histo_list[0].type:
2526 matched = True
2527 histo_list.append(histo)
2528 break
2529 if not matched:
2530 matching_histo_lists.append(HwUList([histo]))
2531
2532
2533
2534 self[:] = []
2535 for histo_group in matching_histo_lists:
2536
2537
2538 if len(histo_group)==1:
2539 self.append(histo_group[0])
2540 continue
2541
2542
2543 if any(hist.jetsample==-1 for hist in histo_group if
2544 hasattr(hist, 'jetsample')):
2545 self.extend(histo_group)
2546 continue
2547 summed_histogram = copy.copy(histo_group[0])
2548 for histo in histo_group[1:]:
2549 summed_histogram = summed_histogram + histo
2550 summed_histogram.jetsample = -1
2551 self.append(summed_histogram)
2552 self.extend(histo_group)
2553
2554
2555 if not jet_samples_to_keep is None:
2556 self[:] = filter(lambda histo:
2557 (not hasattr(histo,'jetsample')) or (histo.jetsample == -1) or
2558 (histo.jetsample in jet_samples_to_keep), self)
2559
2560
2561
2562 def ratio_no_correlations(wgtsA, wgtsB):
2563 new_wgts = {}
2564 for label, wgt in wgtsA.items():
2565 if wgtsB['central']==0.0 and wgt==0.0:
2566 new_wgts[label] = 0.0
2567 continue
2568 elif wgtsB['central']==0.0:
2569
2570
2571
2572 new_wgts[label] = 0.0
2573 continue
2574 new_wgts[label] = (wgtsA[label]/wgtsB['central'])
2575 return new_wgts
2576
2577
2578
2579 n_histograms = len(self)
2580 ratio_histos = HwUList([])
2581
2582 n_ratios_included = 0
2583 for i, histo in enumerate(self[1:]):
2584 if not hasattr(histo,'jetsample') or histo.jetsample==self[0].jetsample:
2585 n_ratios_included += 1
2586 else:
2587 continue
2588
2589 if number_of_ratios >=0 and n_ratios_included > number_of_ratios:
2590 break
2591
2592 if ratio_correlations:
2593 ratio_histos.append(histo/self[0])
2594 else:
2595 ratio_histos.append(self[0].__class__.combine(histo, self[0],
2596 ratio_no_correlations))
2597 if self[0].type=='NLO' and self[1].type=='LO':
2598 ratio_histos[-1].title += '1/K-factor'
2599 elif self[0].type=='LO' and self[1].type=='NLO':
2600 ratio_histos[-1].title += 'K-factor'
2601 else:
2602 ratio_histos[-1].title += ' %s/%s'%(
2603 self[1].type if self[1].type else '(%d)'%(i+2),
2604 self[0].type if self[0].type else '(1)')
2605
2606
2607 ratio_histos[-1].type = 'AUX'
2608 self.extend(ratio_histos)
2609
2610
2611 if 'scale' in uncertainties:
2612 (mu_var_pos,mu) = self[0].set_uncertainty(type='all_scale')
2613 else:
2614 (mu_var_pos,mu) = (None,[None])
2615
2616 if 'pdf' in uncertainties:
2617 (PDF_var_pos,pdf) = self[0].set_uncertainty(type='PDF',lhapdfconfig=lhapdfconfig)
2618 else:
2619 (PDF_var_pos,pdf) = (None,[None])
2620
2621 if 'merging_scale' in uncertainties:
2622 (merging_var_pos,merging) = self[0].set_uncertainty(type='merging')
2623 else:
2624 (merging_var_pos,merging) = (None,[None])
2625 if 'alpsfact' in uncertainties:
2626 (alpsfact_var_pos,alpsfact) = self[0].set_uncertainty(type='alpsfact')
2627 else:
2628 (alpsfact_var_pos,alpsfact) = (None,[None])
2629
2630 uncertainties_present = list(uncertainties)
2631 if PDF_var_pos is None and 'pdf' in uncertainties_present:
2632 uncertainties_present.remove('pdf')
2633 if mu_var_pos is None and 'scale' in uncertainties_present:
2634 uncertainties_present.remove('scale')
2635 if merging_var_pos is None and 'merging' in uncertainties_present:
2636 uncertainties_present.remove('merging')
2637 if alpsfact_var_pos is None and 'alpsfact' in uncertainties_present:
2638 uncertainties_present.remove('alpsfact')
2639 no_uncertainties = len(uncertainties_present)==0
2640
2641
2642 try:
2643 uncertainties_present.remove('statistical')
2644 except:
2645 pass
2646 if use_band is None:
2647
2648
2649 if len(uncertainties_present)==0:
2650 use_band = []
2651 elif len(uncertainties_present)==1:
2652 use_band = uncertainties_present
2653 elif 'scale' in uncertainties_present:
2654 use_band = ['scale']
2655 else:
2656 use_band = [uncertainties_present[0]]
2657
2658 for histo in self[1:]:
2659 if (not mu_var_pos is None) and \
2660 mu_var_pos != histo.set_uncertainty(type='all_scale')[0]:
2661 raise MadGraph5Error, 'Not all histograms in this group specify'+\
2662 ' scale uncertainties. It is required to be able to output them'+\
2663 ' together.'
2664 if (not PDF_var_pos is None) and\
2665 PDF_var_pos != histo.set_uncertainty(type='PDF',\
2666 lhapdfconfig=lhapdfconfig)[0]:
2667 raise MadGraph5Error, 'Not all histograms in this group specify'+\
2668 ' PDF uncertainties. It is required to be able to output them'+\
2669 ' together.'
2670 if (not merging_var_pos is None) and\
2671 merging_var_pos != histo.set_uncertainty(type='merging')[0]:
2672 raise MadGraph5Error, 'Not all histograms in this group specify'+\
2673 ' merging uncertainties. It is required to be able to output them'+\
2674 ' together.'
2675 if (not alpsfact_var_pos is None) and\
2676 alpsfact_var_pos != histo.set_uncertainty(type='alpsfact')[0]:
2677 raise MadGraph5Error, 'Not all histograms in this group specify'+\
2678 ' alpsfact uncertainties. It is required to be able to output them'+\
2679 ' together.'
2680
2681
2682
2683 for i, histo in enumerate(self):
2684
2685 HwU_out.extend(histo.get_HwU_source(\
2686 print_header=(block_position==0 and i==0)))
2687 HwU_out.extend(['',''])
2688
2689
2690 global_header =\
2691 """
2692 ################################################################################
2693 ### Rendering of the plot titled '%(title)s'
2694 ################################################################################
2695
2696 set multiplot
2697 set label "%(title)s" font ",13" at graph 0.04, graph 1.05
2698 set xrange [%(xmin).4e:%(xmax).4e]
2699 set bmargin 0
2700 set tmargin 0
2701 set xtics nomirror
2702 set ytics nomirror
2703 set mytics %(mxtics)d
2704 %(set_xtics)s
2705 set key horizontal noreverse maxcols 1 width -4
2706 set label front 'MadGraph5\_aMC\@NLO' font "Courier,11" rotate by 90 at graph 1.02, graph 0.04
2707 """
2708
2709
2710 subhistogram_header = \
2711 """#-- rendering subhistograms '%(subhistogram_type)s'
2712 %(unset label)s
2713 %(set_format_y)s
2714 set yrange [%(ymin).4e:%(ymax).4e]
2715 set origin %(origin_x).4e, %(origin_y).4e
2716 set size %(size_x).4e, %(size_y).4e
2717 set mytics %(mytics)d
2718 %(set_ytics)s
2719 %(set_format_x)s
2720 %(set_yscale)s
2721 %(set_ylabel)s
2722 %(set_histo_label)s
2723 plot \\"""
2724 replacement_dic = {}
2725
2726 replacement_dic['title'] = self[0].get_HwU_histogram_name(format='human-no_type')
2727
2728
2729 wgts_to_consider = ['central']
2730 if not mu_var_pos is None:
2731 for mu_var in mu_var_pos:
2732 wgts_to_consider.append(self[0].bins.weight_labels[mu_var])
2733 wgts_to_consider.append(self[0].bins.weight_labels[mu_var+1])
2734 wgts_to_consider.append(self[0].bins.weight_labels[mu_var+2])
2735 if not PDF_var_pos is None:
2736 for PDF_var in PDF_var_pos:
2737 wgts_to_consider.append(self[0].bins.weight_labels[PDF_var])
2738 wgts_to_consider.append(self[0].bins.weight_labels[PDF_var+1])
2739 wgts_to_consider.append(self[0].bins.weight_labels[PDF_var+2])
2740 if not merging_var_pos is None:
2741 for merging_var in merging_var_pos:
2742 wgts_to_consider.append(self[0].bins.weight_labels[merging_var])
2743 wgts_to_consider.append(self[0].bins.weight_labels[merging_var+1])
2744 wgts_to_consider.append(self[0].bins.weight_labels[merging_var+2])
2745 if not alpsfact_var_pos is None:
2746 for alpsfact_var in alpsfact_var_pos:
2747 wgts_to_consider.append(self[0].bins.weight_labels[alpsfact_var])
2748 wgts_to_consider.append(self[0].bins.weight_labels[alpsfact_var+1])
2749 wgts_to_consider.append(self[0].bins.weight_labels[alpsfact_var+2])
2750
2751 (xmin, xmax) = HwU.get_x_optimal_range(self[:2],\
2752 weight_labels = wgts_to_consider)
2753 replacement_dic['xmin'] = xmin
2754 replacement_dic['xmax'] = xmax
2755 replacement_dic['mxtics'] = 10
2756 replacement_dic['set_xtics'] = 'set xtics auto'
2757
2758
2759 gnuplot_out.append(global_header%replacement_dic)
2760
2761
2762 replacement_dic['subhistogram_type'] = '%s and %s results'%(
2763 str(self[0].type),str(self[1].type)) if len(self)>1 else \
2764 'single diagram output'
2765 (ymin, ymax) = HwU.get_y_optimal_range(self[:2],
2766 labels = wgts_to_consider, scale=self[0].y_axis_mode)
2767
2768
2769 if ymin< 0.0:
2770 self[0].y_axis_mode = 'LIN'
2771
2772
2773 if self[0].y_axis_mode=='LOG':
2774 ymax += 10.0 * ymax
2775 ymin -= 0.1 * ymin
2776 else:
2777 ymax += 0.3 * (ymax - ymin)
2778 ymin -= 0.3 * (ymax - ymin)
2779
2780 replacement_dic['ymin'] = ymin
2781 replacement_dic['ymax'] = ymax
2782 replacement_dic['unset label'] = ''
2783 (replacement_dic['origin_x'], replacement_dic['origin_y'],
2784 replacement_dic['size_x'], replacement_dic['size_y']) = layout_geometry.pop()
2785 replacement_dic['mytics'] = 10
2786
2787 replacement_dic['set_ytics'] = 'set ytics auto'
2788 replacement_dic['set_format_x'] = "set format x ''" if \
2789 (len(self)-n_histograms>0 or not no_uncertainties) else "set format x"
2790 replacement_dic['set_ylabel'] = 'set ylabel "{/Symbol s} per bin [pb]"'
2791 replacement_dic['set_yscale'] = "set logscale y" if \
2792 self[0].y_axis_mode=='LOG' else 'unset logscale y'
2793 replacement_dic['set_format_y'] = "set format y '10^{%T}'" if \
2794 self[0].y_axis_mode=='LOG' else 'unset format'
2795
2796 replacement_dic['set_histo_label'] = ""
2797 gnuplot_out.append(subhistogram_header%replacement_dic)
2798
2799
2800 plot_lines = []
2801 uncertainty_plot_lines = []
2802 n=-1
2803
2804 for i, histo in enumerate(self[:n_histograms]):
2805 n=n+1
2806 color_index = n%self.number_line_colors_defined+1
2807
2808 title = []
2809 if histo.type is None and not hasattr(histo, 'jetsample'):
2810 title.append('%d'%(i+1))
2811 else:
2812 if histo.type:
2813 title.append('NLO' if \
2814 histo.type.split()[0]=='NLO' else histo.type)
2815 if hasattr(histo, 'jetsample'):
2816 if histo.jetsample!=-1:
2817 title.append('jet sample %d'%histo.jetsample)
2818 else:
2819 title.append('all jet samples')
2820
2821 title = ', '.join(title)
2822
2823 if histo.type is None and not hasattr(histo, 'jetsample'):
2824 major_title = 'central value for plot (%d)'%(i+1)
2825 else:
2826 major_title = []
2827 if not histo.type is None:
2828 major_title.append(histo.type)
2829 if hasattr(histo, 'jetsample'):
2830 if histo.jetsample!=-1:
2831 major_title.append('jet sample %d'%histo.jetsample)
2832 else:
2833 major_title.append('all jet samples')
2834 else:
2835 major_title.append('central value')
2836 major_title = ', '.join(major_title)
2837
2838 if not mu[0] in ['none',None]:
2839 major_title += ', dynamical\_scale\_choice=%s'%mu[0]
2840 if not pdf[0] in ['none',None]:
2841 major_title += ', PDF=%s'%pdf[0].replace('_','\_')
2842
2843
2844
2845 if not (i!=0 and hasattr(histo,'jetsample') and histo.jetsample!=-1 and \
2846 not (jet_samples_to_keep and len(jet_samples_to_keep)==1 and
2847 jet_samples_to_keep[0] == histo.jetsample)):
2848
2849 uncertainty_plot_lines.append({})
2850
2851
2852
2853
2854
2855
2856
2857
2858 if not mu_var_pos is None and len(mu_var_pos)>0:
2859 if 'scale' in use_band:
2860 uncertainty_plot_lines[-1]['scale'] = get_uncertainty_lines(
2861 HwU_name, block_position+i, mu_var_pos[0]+4, color_index+10,
2862 '%s, scale variation'%title, band='scale' in use_band)
2863 else:
2864 uncertainty_plot_lines[-1]['scale'] = \
2865 ["sqrt(-1) ls %d title '%s'"%(color_index+10,'%s, scale variation'%title)]
2866
2867 if not PDF_var_pos is None and len(PDF_var_pos)>0:
2868 if 'pdf' in use_band:
2869 uncertainty_plot_lines[-1]['pdf'] = get_uncertainty_lines(
2870 HwU_name,block_position+i, PDF_var_pos[0]+4, color_index+20,
2871 '%s, PDF variation'%title, band='pdf' in use_band)
2872 else:
2873 uncertainty_plot_lines[-1]['pdf'] = \
2874 ["sqrt(-1) ls %d title '%s'"%(color_index+20,'%s, PDF variation'%title)]
2875
2876 if not merging_var_pos is None and len(merging_var_pos)>0:
2877 if 'merging_scale' in use_band:
2878 uncertainty_plot_lines[-1]['merging_scale'] = get_uncertainty_lines(
2879 HwU_name,block_position+i, merging_var_pos[0]+4, color_index+30,
2880 '%s, merging scale variation'%title, band='merging_scale' in use_band)
2881 else:
2882 uncertainty_plot_lines[-1]['merging_scale'] = \
2883 ["sqrt(-1) ls %d title '%s'"%(color_index+30,'%s, merging scale variation'%title)]
2884
2885 if not alpsfact_var_pos is None and len(alpsfact_var_pos)>0:
2886 if 'alpsfact' in use_band:
2887 uncertainty_plot_lines[-1]['alpsfact'] = get_uncertainty_lines(
2888 HwU_name,block_position+i, alpsfact_var_pos[0]+4, color_index+40,
2889 '%s, alpsfact variation'%title, band='alpsfact' in use_band)
2890 else:
2891 uncertainty_plot_lines[-1]['alpsfact'] = \
2892 ["sqrt(-1) ls %d title '%s'"%(color_index+40,'%s, alpsfact variation'%title)]
2893
2894
2895
2896
2897
2898
2899
2900
2901 plot_lines.extend(
2902 get_main_central_plot_lines(HwU_name, block_position+i,
2903 color_index, major_title, 'statistical' in uncertainties))
2904
2905
2906 if not mu_var_pos is None:
2907 for j,mu_var in enumerate(mu_var_pos):
2908 if j!=0:
2909 n=n+1
2910 color_index = n%self.number_line_colors_defined+1
2911 plot_lines.append(
2912 "'%s' index %d using (($1+$2)/2):%d ls %d title '%s'"\
2913 %(HwU_name,block_position+i,mu_var+3,color_index,\
2914 '%s dynamical\_scale\_choice=%s' % (title,mu[j])))
2915
2916 if not PDF_var_pos is None:
2917 for j,PDF_var in enumerate(PDF_var_pos):
2918 if j!=0:
2919 n=n+1
2920 color_index = n%self.number_line_colors_defined+1
2921 plot_lines.append(
2922 "'%s' index %d using (($1+$2)/2):%d ls %d title '%s'"\
2923 %(HwU_name,block_position+i,PDF_var+3,color_index,\
2924 '%s PDF=%s' % (title,pdf[j].replace('_','\_'))))
2925
2926
2927
2928 for one_plot in uncertainty_plot_lines:
2929 for uncertainty_type, lines in one_plot.items():
2930 if not uncertainty_type in use_band:
2931 plot_lines.extend(lines)
2932
2933 for one_plot in uncertainty_plot_lines:
2934 for uncertainty_type, lines in one_plot.items():
2935 if uncertainty_type in use_band:
2936 plot_lines.extend(lines)
2937
2938
2939 plot_lines.reverse()
2940
2941
2942 gnuplot_out.append(',\\\n'.join(plot_lines))
2943
2944
2945 replacement_dic['subhistogram_type'] = 'Relative scale and PDF uncertainty'
2946
2947 if 'statistical' in uncertainties:
2948 wgts_to_consider.append('stat_error')
2949
2950
2951
2952 def rel_scale(wgtsA, wgtsB):
2953 new_wgts = {}
2954 for label, wgt in wgtsA.items():
2955 if label in wgts_to_consider:
2956 if wgtsB['central']==0.0 and wgt==0.0:
2957 new_wgts[label] = 0.0
2958 continue
2959 elif wgtsB['central']==0.0:
2960
2961
2962
2963 new_wgts[label] = 0.0
2964 continue
2965 new_wgts[label] = (wgtsA[label]/wgtsB['central'])
2966 if label != 'stat_error':
2967 new_wgts[label] -= 1.0
2968 else:
2969 new_wgts[label] = wgtsA[label]
2970 return new_wgts
2971
2972 histos_for_subplots = [(i,histo) for i, histo in enumerate(self[:n_histograms]) if
2973 ( not (i!=0 and hasattr(histo,'jetsample') and histo.jetsample!=-1 and \
2974 not (jet_samples_to_keep and len(jet_samples_to_keep)==1 and
2975 jet_samples_to_keep[0] == histo.jetsample)) )]
2976
2977
2978
2979
2980 (ymin, ymax) = HwU.get_y_optimal_range([histo[1].__class__.combine(
2981 histo[1],histo[1],rel_scale) for histo in histos_for_subplots],
2982 labels = wgts_to_consider, scale='LIN')
2983
2984
2985 ymax = ymax + 0.2 * (ymax - ymin)
2986 ymin = ymin - 0.2 * (ymax - ymin)
2987 replacement_dic['unset label'] = 'unset label'
2988 replacement_dic['ymin'] = ymin
2989 replacement_dic['ymax'] = ymax
2990 if not no_uncertainties:
2991 (replacement_dic['origin_x'], replacement_dic['origin_y'],
2992 replacement_dic['size_x'], replacement_dic['size_y']) = layout_geometry.pop()
2993 replacement_dic['mytics'] = 2
2994
2995 replacement_dic['set_ytics'] = 'set ytics auto'
2996 replacement_dic['set_format_x'] = "set format x ''" if \
2997 len(self)-n_histograms>0 else "set format x"
2998 replacement_dic['set_ylabel'] = 'set ylabel "%s rel.unc."'\
2999 %('(1)' if self[0].type==None else '%s'%('NLO' if \
3000 self[0].type.split()[0]=='NLO' else self[0].type))
3001 replacement_dic['set_yscale'] = "unset logscale y"
3002 replacement_dic['set_format_y'] = 'unset format'
3003
3004
3005 tit='Relative uncertainties w.r.t. central value'
3006 if n_histograms > 1:
3007 tit=tit+'s'
3008
3009
3010
3011
3012 replacement_dic['set_histo_label'] = \
3013 'set label "%s" font ",9" front at graph 0.03, graph 0.13' % tit
3014
3015
3016 if not no_uncertainties:
3017 gnuplot_out.append(subhistogram_header%replacement_dic)
3018
3019
3020 plot_lines = []
3021 uncertainty_plot_lines = []
3022 n=-1
3023 for (i,histo) in histos_for_subplots:
3024 n=n+1
3025 k=n
3026 color_index = n%self.number_line_colors_defined+1
3027
3028 if not mu_var_pos is None:
3029 for j,mu_var in enumerate(mu_var_pos):
3030 uncertainty_plot_lines.append({})
3031 if j==0:
3032 color_index = k%self.number_line_colors_defined+1
3033 else:
3034 n=n+1
3035 color_index = n%self.number_line_colors_defined+1
3036
3037 if j>0 or mu[j]!='none':
3038 plot_lines.append(
3039 "'%s' index %d using (($1+$2)/2):(safe($%d,$3,1.0)-1.0) ls %d title ''"\
3040 %(HwU_name,block_position+i,mu_var+3,color_index))
3041 uncertainty_plot_lines[-1]['scale'] = get_uncertainty_lines(
3042 HwU_name, block_position+i, mu_var+4, color_index+10,'',
3043 ratio=True, band='scale' in use_band)
3044 if not PDF_var_pos is None:
3045 for j,PDF_var in enumerate(PDF_var_pos):
3046 uncertainty_plot_lines.append({})
3047 if j==0:
3048 color_index = k%self.number_line_colors_defined+1
3049 else:
3050 n=n+1
3051 color_index = n%self.number_line_colors_defined+1
3052
3053 if j>0 or pdf[j]!='none':
3054 plot_lines.append(
3055 "'%s' index %d using (($1+$2)/2):(safe($%d,$3,1.0)-1.0) ls %d title ''"\
3056 %(HwU_name,block_position+i,PDF_var+3,color_index))
3057 uncertainty_plot_lines[-1]['pdf'] = get_uncertainty_lines(
3058 HwU_name, block_position+i, PDF_var+4, color_index+20,'',
3059 ratio=True, band='pdf' in use_band)
3060 if not merging_var_pos is None:
3061 for j,merging_var in enumerate(merging_var_pos):
3062 uncertainty_plot_lines.append({})
3063 if j==0:
3064 color_index = k%self.number_line_colors_defined+1
3065 else:
3066 n=n+1
3067 color_index = n%self.number_line_colors_defined+1
3068 if j>0 or merging[j]!='none':
3069 plot_lines.append(
3070 "'%s' index %d using (($1+$2)/2):(safe($%d,$3,1.0)-1.0) ls %d title ''"\
3071 %(HwU_name,block_position+i,merging_var+3,color_index))
3072 uncertainty_plot_lines[-1]['merging_scale'] = get_uncertainty_lines(
3073 HwU_name, block_position+i, merging_var+4, color_index+30,'',
3074 ratio=True, band='merging_scale' in use_band)
3075 if not alpsfact_var_pos is None:
3076 for j,alpsfact_var in enumerate(alpsfact_var_pos):
3077 uncertainty_plot_lines.append({})
3078 if j==0:
3079 color_index = k%self.number_line_colors_defined+1
3080 else:
3081 n=n+1
3082 color_index = n%self.number_line_colors_defined+1
3083 if j>0 or alpsfact[j]!='none':
3084 plot_lines.append(
3085 "'%s' index %d using (($1+$2)/2):(safe($%d,$3,1.0)-1.0) ls %d title ''"\
3086 %(HwU_name,block_position+i,alpsfact_var+3,color_index))
3087 uncertainty_plot_lines[-1]['alpsfact'] = get_uncertainty_lines(
3088 HwU_name, block_position+i, alpsfact_var+4, color_index+40,'',
3089 ratio=True, band='alpsfact' in use_band)
3090
3091 if 'statistical' in uncertainties:
3092 plot_lines.append(
3093 "'%s' index %d using (($1+$2)/2):(0.0):(safe($4,$3,0.0)) w yerrorbar ls %d title ''"%\
3094 (HwU_name,block_position+i,color_index))
3095
3096 plot_lines.append("0.0 ls 999 title ''")
3097
3098
3099
3100 for one_plot in uncertainty_plot_lines:
3101 for uncertainty_type, lines in one_plot.items():
3102 if not uncertainty_type in use_band:
3103 plot_lines.extend(lines)
3104
3105 for one_plot in uncertainty_plot_lines:
3106 for uncertainty_type, lines in one_plot.items():
3107 if uncertainty_type in use_band:
3108 plot_lines.extend(lines)
3109
3110
3111 plot_lines.reverse()
3112
3113 if not no_uncertainties:
3114 gnuplot_out.append(',\\\n'.join(plot_lines))
3115
3116
3117 if len(self)-n_histograms==0:
3118
3119 gnuplot_out.extend(['','unset label','',
3120 '################################################################################'])
3121
3122 return block_position+len(self)
3123
3124
3125 ratio_name_long='('
3126 for i, histo in enumerate(self[:n_histograms]):
3127 if i==0: continue
3128 ratio_name_long+='%d'%(i+1) if histo.type is None else ('NLO' if \
3129 histo.type.split()[0]=='NLO' else histo.type)
3130 ratio_name_long+=')/'
3131 ratio_name_long+=('(1' if self[0].type==None else '(%s'%('NLO' if \
3132 self[0].type.split()[0]=='NLO' else self[0].type))+' central value)'
3133
3134 ratio_name_short = 'ratio w.r.t. '+('1' if self[0].type==None else '%s'%('NLO' if \
3135 self[0].type.split()[0]=='NLO' else self[0].type))
3136
3137 replacement_dic['subhistogram_type'] = '%s ratio'%ratio_name_long
3138 replacement_dic['set_ylabel'] = 'set ylabel "%s"'%ratio_name_short
3139
3140 (ymin, ymax) = HwU.get_y_optimal_range(self[n_histograms:],
3141 labels = wgts_to_consider, scale='LIN',Kratio = True)
3142
3143
3144 ymax = ymax + 0.2 * (ymax - ymin)
3145 ymin = ymin - 0.2 * (ymax - ymin)
3146 replacement_dic['unset label'] = 'unset label'
3147 replacement_dic['ymin'] = ymin
3148 replacement_dic['ymax'] = ymax
3149 (replacement_dic['origin_x'], replacement_dic['origin_y'],
3150 replacement_dic['size_x'], replacement_dic['size_y']) = layout_geometry.pop()
3151 replacement_dic['mytics'] = 2
3152
3153 replacement_dic['set_ytics'] = 'set ytics auto'
3154 replacement_dic['set_format_x'] = "set format x"
3155 replacement_dic['set_yscale'] = "unset logscale y"
3156 replacement_dic['set_format_y'] = 'unset format'
3157 replacement_dic['set_histo_label'] = \
3158 'set label "%s" font ",9" at graph 0.03, graph 0.13'%ratio_name_long
3159
3160 gnuplot_out.append(subhistogram_header%replacement_dic)
3161
3162 uncertainty_plot_lines = []
3163 plot_lines = []
3164
3165
3166 n=-1
3167 n=n+1
3168 if not mu_var_pos is None:
3169 for j,mu_var in enumerate(mu_var_pos):
3170 if j!=0: n=n+1
3171 if not PDF_var_pos is None:
3172 for j,PDF_var in enumerate(PDF_var_pos):
3173 if j!=0: n=n+1
3174 if not merging_var_pos is None:
3175 for j,merging_var in enumerate(merging_var_pos):
3176 if j!=0: n=n+1
3177 if not alpsfact_var_pos is None:
3178 for j,alpsfact_var in enumerate(alpsfact_var_pos):
3179 if j!=0: n=n+1
3180
3181 for i_histo_ratio, histo_ration in enumerate(self[n_histograms:]):
3182 n=n+1
3183 k=n
3184 block_ratio_pos = block_position+n_histograms+i_histo_ratio
3185 color_index = n%self.number_line_colors_defined+1
3186
3187 plot_lines.append(
3188 "'%s' index %d using (($1+$2)/2):3 ls %d title ''"%\
3189 (HwU_name,block_ratio_pos,color_index))
3190 if 'statistical' in uncertainties:
3191 plot_lines.append(
3192 "'%s' index %d using (($1+$2)/2):3:4 w yerrorbar ls %d title ''"%\
3193 (HwU_name,block_ratio_pos,color_index))
3194
3195
3196 if not mu_var_pos is None:
3197 for j,mu_var in enumerate(mu_var_pos):
3198 uncertainty_plot_lines.append({})
3199 if j==0:
3200 color_index = k%self.number_line_colors_defined+1
3201 else:
3202 n=n+1
3203 color_index = n%self.number_line_colors_defined+1
3204
3205 if j>0 or mu[j]!='none':
3206 plot_lines.append(
3207 "'%s' index %d using (($1+$2)/2):%d ls %d title ''"\
3208 %(HwU_name,block_ratio_pos,mu_var+3,color_index))
3209 uncertainty_plot_lines[-1]['scale'] = get_uncertainty_lines(
3210 HwU_name, block_ratio_pos, mu_var+4, color_index+10,'',
3211 band='scale' in use_band)
3212 if not PDF_var_pos is None:
3213 for j,PDF_var in enumerate(PDF_var_pos):
3214 uncertainty_plot_lines.append({})
3215 if j==0:
3216 color_index = k%self.number_line_colors_defined+1
3217 else:
3218 n=n+1
3219 color_index = n%self.number_line_colors_defined+1
3220
3221 if j>0 or pdf[j]!='none':
3222 plot_lines.append(
3223 "'%s' index %d using (($1+$2)/2):%d ls %d title ''"\
3224 %(HwU_name,block_ratio_pos,PDF_var+3,color_index))
3225 uncertainty_plot_lines[-1]['pdf'] = get_uncertainty_lines(
3226 HwU_name, block_ratio_pos, PDF_var+4, color_index+20,'',
3227 band='pdf' in use_band)
3228 if not merging_var_pos is None:
3229 for j,merging_var in enumerate(merging_var_pos):
3230 uncertainty_plot_lines.append({})
3231 if j==0:
3232 color_index = k%self.number_line_colors_defined+1
3233 else:
3234 n=n+1
3235 color_index = n%self.number_line_colors_defined+1
3236 if j>0 or merging[j]!='none':
3237 plot_lines.append(
3238 "'%s' index %d using (($1+$2)/2):%d ls %d title ''"\
3239 %(HwU_name,block_ratio_pos,merging_var+3,color_index))
3240 uncertainty_plot_lines[-1]['merging_scale'] = get_uncertainty_lines(
3241 HwU_name, block_ratio_pos, merging_var+4, color_index+30,'',
3242 band='merging_scale' in use_band)
3243 if not alpsfact_var_pos is None:
3244 for j,alpsfact_var in enumerate(alpsfact_var_pos):
3245 uncertainty_plot_lines.append({})
3246 if j==0:
3247 color_index = k%self.number_line_colors_defined+1
3248 else:
3249 n=n+1
3250 color_index = n%self.number_line_colors_defined+1
3251 if j>0 or alpsfact[j]!='none':
3252 plot_lines.append(
3253 "'%s' index %d using (($1+$2)/2):%d ls %d title ''"\
3254 %(HwU_name,block_ratio_pos,alpsfact_var+3,color_index))
3255 uncertainty_plot_lines[-1]['alpsfact'] = get_uncertainty_lines(
3256 HwU_name, block_ratio_pos, alpsfact_var+4, color_index+40,'',
3257 band='alpsfact' in use_band)
3258
3259
3260
3261 for one_plot in uncertainty_plot_lines:
3262 for uncertainty_type, lines in one_plot.items():
3263 if not uncertainty_type in use_band:
3264 plot_lines.extend(lines)
3265
3266 for one_plot in uncertainty_plot_lines:
3267 for uncertainty_type, lines in one_plot.items():
3268 if uncertainty_type in use_band:
3269 plot_lines.extend(lines)
3270
3271 plot_lines.append("1.0 ls 999 title ''")
3272
3273
3274 plot_lines.reverse()
3275
3276 gnuplot_out.append(',\\\n'.join(plot_lines))
3277
3278
3279 gnuplot_out.extend(['','unset label','',
3280 '################################################################################'])
3281
3282
3283 return block_position+len(self)
3284
3285
3286
3287
3288 -def plot_ratio_from_HWU(path, ax, hwu_variable, hwu_numerator, hwu_denominator, *args, **opts):
3289 """INPUT:
3290 - path can be a path to HwU or an HwUList instance
3291 - ax is the matplotlib frame where to do the plot
3292 - hwu_variable is the histograms to consider
3293 - hwu_numerator is the numerator of the ratio plot
3294 - hwu_denominator is the denominator of the ratio plot
3295 OUTPUT:
3296 - adding the curves to the plot
3297 - return the HwUList
3298 """
3299
3300 if isinstance(path, str):
3301 hwu = HwUList(path, raw_labels=True)
3302 else:
3303 hwu = path
3304
3305 if 'hwu_denominator_path' in opts:
3306 print 'found second hwu'
3307 if isinstance(opts['hwu_denominator_path'],str):
3308 hwu2 = HwUList(path, raw_labels=True)
3309 else:
3310 hwu2 = opts['hwu_denominator_path']
3311 del opts['hwu_denominator_path']
3312 else:
3313 hwu2 = hwu
3314
3315
3316 select_hist = hwu.get(hwu_variable)
3317 select_hist2 = hwu2.get(hwu_variable)
3318 bins = select_hist.get('bins')
3319 num = select_hist.get(hwu_numerator)
3320 denom = select_hist2.get(hwu_denominator)
3321 ratio = [num[i]/denom[i] if denom[i] else 1 for i in xrange(len(bins))]
3322 if 'drawstyle' not in opts:
3323 opts['drawstyle'] = 'steps'
3324 ax.plot(bins, ratio, *args, **opts)
3325 return hwu
3326
3327 -def plot_from_HWU(path, ax, hwu_variable, hwu_central, *args, **opts):
3328 """INPUT:
3329 - path can be a path to HwU or an HwUList instance
3330 - ax is the matplotlib frame where to do the plot
3331 - hwu_variable is the histograms to consider
3332 - hwu_central is the central curve to consider
3333 - hwu_error is the error band to consider (optional: Default is no band)
3334 - hwu_error_mode is how to compute the error band (optional)
3335 OUTPUT:
3336 - adding the curves to the plot
3337 - return the HwUList
3338 - return the line associated to the central (can be used to get the color)
3339 """
3340
3341
3342 if 'hwu_error' in opts:
3343 hwu_error = opts['hwu_error']
3344 del opts['hwu_error']
3345 else:
3346 hwu_error = None
3347
3348 if 'hwu_error_mode' in opts:
3349 hwu_error_mode = opts['hwu_error_mode']
3350 del opts['hwu_error_mode']
3351 else:
3352 hwu_error_mode = None
3353
3354 if 'hwu_mult' in opts:
3355 hwu_mult = opts['hwu_mult']
3356 del opts['hwu_mult']
3357 else:
3358 hwu_mult = 1
3359
3360 if isinstance(path, str):
3361 hwu = HwUList(path, raw_labels=True)
3362 else:
3363 hwu = path
3364
3365
3366 select_hist = hwu.get(hwu_variable)
3367 bins = select_hist.get('bins')
3368 central_value = select_hist.get(hwu_central)
3369 if hwu_mult != 1:
3370 central_value = [hwu_mult*b for b in central_value]
3371 if 'drawstyle' not in opts:
3372 opts['drawstyle'] = 'steps'
3373 H, = ax.plot(bins, central_value, *args, **opts)
3374
3375
3376 if hwu_error:
3377 if not 'hwu_error_mode' in opts:
3378 opts['hwu_error_mode']=None
3379 h_min, h_max = select_hist.get_uncertainty_band(hwu_error, mode=hwu_error_mode)
3380 if hwu_mult != 1:
3381 h_min = [hwu_mult*b for b in h_min]
3382 h_max = [hwu_mult*b for b in h_max]
3383 fill_between_steps(bins, h_min, h_max, ax=ax, facecolor=H.get_color(),
3384 alpha=0.5, edgecolor=H.get_color(),hatch='/')
3385
3386 return hwu, H
3387
3388
3389
3390
3391
3392
3393 if __name__ == "__main__":
3394 main_doc = \
3395 """ For testing and standalone use. Usage:
3396 python histograms.py <.HwU input_file_path_1> <.HwU input_file_path_2> ... --out=<output_file_path.format> <options>
3397 Where <options> can be a list of the following:
3398 '--help' See this message.
3399 '--gnuplot' or '' output the histograms read to gnuplot
3400 '--HwU' to output the histograms read to the raw HwU source.
3401 '--types=<type1>,<type2>,...' to keep only the type<i> when importing histograms.
3402 '--titles=<title1>,<title2>,...' to keep only the titles which have any of 'title<i>' in them (not necessarily equal to them)
3403 '--n_ratios=<integer>' Specifies how many curves must be considerd for the ratios.
3404 '--no_open' Turn off the automatic processing of the gnuplot output.
3405 '--show_full' to show the complete output of what was read.
3406 '--show_short' to show a summary of what was read.
3407 '--simple_ratios' to turn off correlations and error propagation in the ratio.
3408 '--sum' To sum all identical histograms together
3409 '--average' To average over all identical histograms
3410 '--rebin=<n>' Rebin the plots by merging n-consecutive bins together.
3411 '--assign_types=<type1>,<type2>,...' to assign a type to all histograms of the first, second, etc... files loaded.
3412 '--multiply=<fact1>,<fact2>,...' to multiply all histograms of the first, second, etc... files by the fact1, fact2, etc...
3413 '--no_suffix' Do no add any suffix (like '#1, #2, etc..) to the histograms types.
3414 '--lhapdf-config=<PATH_TO_LHAPDF-CONFIG>' give path to lhapdf-config to compute PDF certainties using LHAPDF (only for lhapdf6)
3415 '--jet_samples=[int1,int2]' Specifies what jet samples to keep. 'None' is the default and keeps them all.
3416 '--central_only' This option specifies to disregard all extra weights, so as to make it possible
3417 to take the ratio of plots with different extra weights specified.
3418 '--keep_all_weights' This option specifies to keep in the HwU produced all the weights, even
3419 those which are not known (i.e. that is scale, PDF or merging variation)
3420 For chosing what kind of variation you want to see on your plot, you can use the following options
3421 '--no_<type>' Turn off the plotting of variations of the chosen type
3422 '--only_<type>' Turn on only the plotting of variations of the chosen type
3423 '--variations=['<type1>',...]' Turn on only the plotting of the variations of the list of chosen types
3424 '--band=['<type1>',...]' Chose for which variations one should use uncertainty bands as opposed to lines
3425 The types can be: pdf, scale, stat, merging or alpsfact
3426 For the last two options one can use ...=all to automatically select all types.
3427
3428 When parsing an XML-formatted plot source output by the Pythia8 driver, the file names can be appended
3429 options as suffixes separated by '|', as follows:
3430 python histograms.py <XML_source_file_name>@<option1>@<option2>@etc..
3431 These options can be
3432 'run_id=<integer>' Specifies the run_ID from which the plots should be loaded.
3433 By default, the first run is considered and the ones that follow are ignored.
3434 'merging_scale=<float>' This option allows to specify to import only the plots corresponding to a specific
3435 value for the merging scale.
3436 A value of -1 means that only the weights with the same merging scale as the central weight are kept.
3437 By default, all weights are considered.
3438 """
3439
3440 possible_options=['--help', '--gnuplot', '--HwU', '--types','--n_ratios',\
3441 '--no_open','--show_full','--show_short','--simple_ratios','--sum','--average','--rebin', \
3442 '--assign_types','--multiply','--no_suffix', '--out', '--jet_samples',
3443 '--no_scale','--no_pdf','--no_stat','--no_merging','--no_alpsfact',
3444 '--only_scale','--only_pdf','--only_stat','--only_merging','--only_alpsfact',
3445 '--variations','--band','--central_only', '--lhapdf-config','--titles',
3446 '--keep_all_weights']
3447 n_ratios = -1
3448 uncertainties = ['scale','pdf','statistical','merging_scale','alpsfact']
3449
3450 use_band = None
3451 auto_open = True
3452 ratio_correlations = True
3453 consider_reweights = ['pdf','scale','murmuf_scales','merging_scale','alpsfact']
3454
3455 - def log(msg):
3456 print "histograms.py :: %s"%str(msg)
3457
3458 if '--help' in sys.argv or len(sys.argv)==1:
3459 log('\n\n%s'%main_doc)
3460 sys.exit(0)
3461
3462 for arg in sys.argv[1:]:
3463 if arg.startswith('--'):
3464 if arg.split('=')[0] not in possible_options:
3465 log('WARNING: option "%s" not valid. It will be ignored' % arg)
3466
3467 arg_string=' '.join(sys.argv)
3468
3469 OutName = ""
3470 for arg in sys.argv[1:]:
3471 if arg.startswith('--out='):
3472 OutName = arg[6:]
3473
3474 accepted_types = []
3475 for arg in sys.argv[1:]:
3476 if arg.startswith('--types='):
3477 accepted_types = [(type if type!='None' else None) for type in \
3478 arg[8:].split(',')]
3479
3480 accepted_titles = []
3481 for arg in sys.argv[1:]:
3482 if arg.startswith('--titles='):
3483 accepted_titles = [(type if type!='None' else None) for type in \
3484 arg[9:].split(',')]
3485
3486 assigned_types = []
3487 for arg in sys.argv[1:]:
3488 if arg.startswith('--assign_types='):
3489 assigned_types = [(type if type!='None' else None) for type in \
3490 arg[15:].split(',')]
3491
3492 jet_samples_to_keep = None
3493
3494 lhapdfconfig = ['lhapdf-config']
3495 for arg in sys.argv[1:]:
3496 if arg.startswith('--lhapdf-config='):
3497 lhapdfconfig = arg[16:]
3498
3499 no_suffix = False
3500 if '--no_suffix' in sys.argv:
3501 no_suffix = True
3502
3503 if '--central_only' in sys.argv:
3504 consider_reweights = []
3505
3506 if '--keep_all_weights' in sys.argv:
3507 consider_reweights = 'ALL'
3508
3509 for arg in sys.argv[1:]:
3510 if arg.startswith('--n_ratios='):
3511 n_ratios = int(arg[11:])
3512
3513 if '--no_open' in sys.argv:
3514 auto_open = False
3515
3516 variation_type_map={'scale':'scale','merging':'merging_scale','pdf':'pdf',
3517 'stat':'statistical','alpsfact':'alpsfact'}
3518
3519 for arg in sys.argv:
3520 try:
3521 opt, value = arg.split('=')
3522 except ValueError:
3523 continue
3524 if opt=='--jet_samples':
3525 jet_samples_to_keep = eval(value)
3526 if opt=='--variations':
3527 uncertainties=[variation_type_map[type] for type in eval(value,
3528 dict([(key,key) for key in variation_type_map.keys()]+
3529 [('all',variation_type_map.keys())]))]
3530 if opt=='--band':
3531 use_band=[variation_type_map[type] for type in eval(value,
3532 dict([(key,key) for key in variation_type_map.keys()]+
3533 [('all',[type for type in variation_type_map.keys() if type!='stat'])]))]
3534
3535 if '--simple_ratios' in sys.argv:
3536 ratio_correlations = False
3537
3538 for arg in sys.argv:
3539 if arg.startswith('--no_') and not arg.startswith('--no_open'):
3540 uncertainties.remove(variation_type_map[arg[5:]])
3541 if arg.startswith('--only_'):
3542 uncertainties= [variation_type_map[arg[7:]]]
3543 break
3544
3545
3546
3547 if isinstance(consider_reweights, list):
3548 naming_map={'pdf':'pdf','scale':'scale',
3549 'merging_scale':'merging_scale','alpsfact':'alpsfact'}
3550 for key in naming_map:
3551 if (not key in uncertainties) and (naming_map[key] in consider_reweights):
3552 consider_reweights.remove(naming_map[key])
3553
3554 n_files = len([_ for _ in sys.argv[1:] if not _.startswith('--')])
3555 histo_norm = [1.0]*n_files
3556
3557 for arg in sys.argv[1:]:
3558 if arg.startswith('--multiply='):
3559 histo_norm = [(float(fact) if fact!='' else 1.0) for fact in \
3560 arg[11:].split(',')]
3561
3562 if '--average' in sys.argv:
3563 histo_norm = [hist/float(n_files) for hist in histo_norm]
3564
3565 log("=======")
3566 histo_list = HwUList([])
3567 for i, arg in enumerate(sys.argv[1:]):
3568 if arg.startswith('--'):
3569 break
3570 log("Loading histograms from '%s'."%arg)
3571 if OutName=="":
3572 OutName = os.path.basename(arg).split('.')[0]+'_output'
3573
3574 file_specification = arg.split('@')
3575 filename = file_specification.pop(0)
3576 file_options = {}
3577 for option in file_specification:
3578 opt, value = option.split('=')
3579 if opt=='run_id':
3580 file_options[opt]=int(value)
3581 if opt=='merging_scale':
3582 file_options[opt]=float(value)
3583 else:
3584 log("Unreckognize file option '%s'."%option)
3585 sys.exit(1)
3586 new_histo_list = HwUList(filename, accepted_types_order=accepted_types,
3587 consider_reweights=consider_reweights, **file_options)
3588
3589 if len(accepted_titles)>0:
3590 new_histo_list = HwUList(histo for histo in new_histo_list if
3591 any(t in histo.title for t in accepted_titles))
3592 for histo in new_histo_list:
3593 if no_suffix or n_files==1:
3594 continue
3595 if not histo.type is None:
3596 histo.type += '|'
3597 else:
3598 histo.type = ''
3599
3600
3601
3602
3603
3604
3605 try:
3606 suffix = assigned_types[i]
3607 except IndexError:
3608 suffix = "#%d"%(i+1)
3609 try:
3610 histo.type = histo.type[:histo.type.index('#')] + suffix
3611 except ValueError:
3612 histo.type += suffix
3613
3614 if i==0 or all(_ not in ['--sum','--average'] for _ in sys.argv):
3615 for j,hist in enumerate(new_histo_list):
3616 new_histo_list[j]=hist*histo_norm[i]
3617 histo_list.extend(new_histo_list)
3618 continue
3619
3620 if any(_ in sys.argv for _ in ['--sum','--average']):
3621 for j, hist in enumerate(new_histo_list):
3622
3623 hist.test_plot_compability(histo_list[j])
3624
3625 histo_list[j] += hist*histo_norm[i]
3626
3627 log("A total of %i histograms were found."%len(histo_list))
3628 log("=======")
3629
3630 n_rebin = 1
3631 for arg in sys.argv[1:]:
3632 if arg.startswith('--rebin='):
3633 n_rebin = int(arg[8:])
3634
3635 if n_rebin > 1:
3636 for hist in histo_list:
3637 hist.rebin(n_rebin)
3638
3639 if '--gnuplot' in sys.argv or all(arg not in ['--HwU'] for arg in sys.argv):
3640
3641 histo_list.output(OutName, format='gnuplot',
3642 number_of_ratios = n_ratios,
3643 uncertainties=uncertainties,
3644 ratio_correlations=ratio_correlations,
3645 arg_string=arg_string,
3646 jet_samples_to_keep=jet_samples_to_keep,
3647 use_band=use_band,
3648 auto_open=auto_open,
3649 lhapdfconfig=lhapdfconfig)
3650
3651 log("%d histograms have been output in " % len(histo_list)+\
3652 "the gnuplot format at '%s.[HwU|gnuplot]'." % OutName)
3653 if auto_open:
3654 command = 'gnuplot %s.gnuplot'%OutName
3655 try:
3656 subprocess.call(command,shell=True,stderr=subprocess.PIPE)
3657 except:
3658 log("Automatic processing of the gnuplot card failed. Try the"+\
3659 " command by hand:\n%s"%command)
3660 else:
3661 sys.exit(0)
3662
3663 if '--HwU' in sys.argv:
3664 log("Histograms data has been output in the HwU format at "+\
3665 "'%s.HwU'."%OutName)
3666 histo_list.output(OutName, format='HwU')
3667 sys.exit(0)
3668
3669 if '--show_short' in sys.argv or '--show_full' in sys.argv:
3670 for i, histo in enumerate(histo_list):
3671 if i!=0:
3672 log('-------')
3673 log(histo.nice_string(short=(not '--show_full' in sys.argv)))
3674 log("=======")
3679 ''' Fills a hole in matplotlib: fill_between for step plots.
3680 Parameters :
3681 ------------
3682 x : array-like
3683 Array/vector of index values. These are assumed to be equally-spaced.
3684 If not, the result will probably look weird...
3685 y1 : array-like
3686 Array/vector of values to be filled under.
3687 y2 : array-Like
3688 Array/vector or bottom values for filled area. Default is 0.
3689 **kwargs will be passed to the matplotlib fill_between() function.
3690 '''
3691
3692 if ax is None:
3693 ax = plt.gca()
3694
3695
3696
3697
3698 xx= []; [(xx.append(d),xx.append(d)) for d in x]; xx = xx[1:]
3699
3700 xstep = x[1] -x[0]
3701
3702 xx.append(xx[-1] + xstep)
3703
3704
3705 if h_align == 'mid':
3706 xx = [X-xstep/2. for X in xx]
3707 elif h_align == 'right':
3708 xx = [X-xstep for X in xx]
3709
3710
3711 yy1 = []; [(yy1.append(d),yy1.append(d)) for d in y1]
3712 if isinstance(y1, list):
3713 yy2 = []; [(yy2.append(d),yy2.append(d)) for d in y2]
3714 else:
3715 yy2=y2
3716 if len(yy2) != len(yy1):
3717 yy2 = []; [(yy2.append(d),yy2.append(d)) for d in y2]
3718
3719
3720 ax.fill_between(xx, yy1, y2=yy2, **kwargs)
3721
3722 return ax
3723
3724