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