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, 91931):
1439
1440 pdf_stdev = 0.0
1441 for pdf in pdfs[1:]:
1442 pdf_stdev += (pdf - cntrl_val)**2
1443 pdf_stdev = math.sqrt(pdf_stdev)
1444 pdf_up = cntrl_val+pdf_stdev
1445 pdf_down = cntrl_val-pdf_stdev
1446 else:
1447
1448 pdf_stdev = 0.0
1449 for pdf in pdfs[1:]:
1450 pdf_stdev += (pdf - cntrl_val)**2
1451 pdf_stdev = math.sqrt(pdf_stdev/float(len(pdfs)-2))
1452 pdf_up = cntrl_val+pdf_stdev
1453 pdf_down = cntrl_val-pdf_stdev
1454
1455 bin.wgts[new_wgt_labels[0]] = bin.wgts[wgts[0]]
1456 bin.wgts[new_wgt_labels[1]] = pdf_down
1457 bin.wgts[new_wgt_labels[2]] = pdf_up
1458
1459
1460
1461 return (position,labels)
1462
1464 """ Select a specific merging scale for the central value of this Histogram. """
1465 if selected_label not in self.bins.weight_labels:
1466 raise MadGraph5Error("Selected weight label '%s' could not be found in this HwU."%selected_label)
1467
1468 for bin in self.bins:
1469 bin.wgts['central']=bin.wgts[selected_label]
1470
1471 - def rebin(self, n_rebin):
1472 """ Rebin the x-axis so as to merge n_rebin consecutive bins into a
1473 single one. """
1474
1475 if n_rebin < 1 or not isinstance(n_rebin, int):
1476 raise MadGraph5Error("The argument 'n_rebin' of the HwU function"+\
1477 " 'rebin' must be larger or equal to 1, not '%s'."%str(n_rebin))
1478 elif n_rebin==1:
1479 return
1480
1481 if self.type and 'NOREBIN' in self.type.upper():
1482 return
1483
1484 rebinning_list = list(range(0,len(self.bins),n_rebin))+[len(self.bins),]
1485 concat_list = [self.bins[rebinning_list[i]:rebinning_list[i+1]] for \
1486 i in range(len(rebinning_list)-1)]
1487
1488 new_bins = copy.copy(self.bins)
1489 del new_bins[:]
1490
1491 for bins_to_merge in concat_list:
1492 if len(bins_to_merge)==0:
1493 continue
1494 new_bins.append(Bin(boundaries=(bins_to_merge[0].boundaries[0],
1495 bins_to_merge[-1].boundaries[1]),wgts={'central':0.0}))
1496 for weight in self.bins.weight_labels:
1497 if weight != 'stat_error':
1498 new_bins[-1].wgts[weight] = \
1499 sum(b.wgts[weight] for b in bins_to_merge)
1500 else:
1501 new_bins[-1].wgts['stat_error'] = \
1502 math.sqrt(sum(b.wgts['stat_error']**2 for b in\
1503 bins_to_merge))
1504
1505 self.bins = new_bins
1506
1507 @classmethod
1509 """ Function to determine the optimal x-axis range when plotting
1510 together the histos in histo_list and considering the weights
1511 weight_labels"""
1512
1513
1514 if weight_labels is None:
1515 weight_labels = histo_list[0].bins.weight_labels
1516
1517 all_boundaries = sum([ list(bin.boundaries) for histo in histo_list \
1518 for bin in histo.bins if \
1519 (sum(abs(bin.wgts[label]) for label in weight_labels) > 0.0)] ,[])
1520
1521 if len(all_boundaries)==0:
1522 all_boundaries = sum([ list(bin.boundaries) for histo in histo_list \
1523 for bin in histo.bins],[])
1524 if len(all_boundaries)==0:
1525 raise MadGraph5Error("The histograms with title '%s'"\
1526 %histo_list[0].title+" seems to have no bins.")
1527
1528 x_min = min(all_boundaries)
1529 x_max = max(all_boundaries)
1530
1531 return (x_min, x_max)
1532
1533 @classmethod
1536 """ Function to determine the optimal y-axis range when plotting
1537 together the histos in histo_list and considering the weights
1538 weight_labels. The option Kratio is present to allow for the couple of
1539 tweaks necessary for the the K-factor ratio histogram y-range."""
1540
1541
1542 if labels is None:
1543 weight_labels = histo_list[0].bins.weight_labels
1544 else:
1545 weight_labels = labels
1546
1547 all_weights = []
1548 for histo in histo_list:
1549 for bin in histo.bins:
1550 for label in weight_labels:
1551
1552
1553 if Kratio and bin.wgts[label]==0.0:
1554 continue
1555 if scale!='LOG':
1556 all_weights.append(bin.wgts[label])
1557 if label == 'stat_error':
1558 all_weights.append(-bin.wgts[label])
1559 elif bin.wgts[label]>0.0:
1560 all_weights.append(bin.wgts[label])
1561
1562
1563 sum([ [bin.wgts[label] for label in weight_labels if \
1564 (scale!='LOG' or bin.wgts[label]!=0.0)] \
1565 for histo in histo_list for bin in histo.bins], [])
1566
1567 all_weights.sort()
1568 if len(all_weights)!=0:
1569 partial_max = all_weights[int(len(all_weights)*0.95)]
1570 partial_min = all_weights[int(len(all_weights)*0.05)]
1571 max = all_weights[-1]
1572 min = all_weights[0]
1573 else:
1574 if scale!='LOG':
1575 return (0.0,1.0)
1576 else:
1577 return (1.0,10.0)
1578
1579 y_max = 0.0
1580 y_min = 0.0
1581
1582
1583 if (max-partial_max)>2.0*(partial_max-partial_min):
1584 y_max = partial_max
1585 else:
1586 y_max = max
1587
1588
1589 if (partial_min - min)>2.0*(partial_max-partial_min) and min != 0.0:
1590 y_min = partial_min
1591 else:
1592 y_min = min
1593
1594 if Kratio:
1595 median = all_weights[len(all_weights)//2]
1596 spread = (y_max-y_min)
1597 if abs(y_max-median)<spread*0.05 or abs(median-y_min)<spread*0.05:
1598 y_max = median + spread/2.0
1599 y_min = median - spread/2.0
1600 if y_min != y_max:
1601 return ( y_min , y_max )
1602
1603
1604 if len(histo_list[0].bins) <= 5:
1605 y_min = min
1606 y_max = max
1607
1608
1609 if y_min == y_max:
1610 if max == min:
1611 y_min -= 1.0
1612 y_max += 1.0
1613 else:
1614 y_min = min
1615 y_max = max
1616
1617 return ( y_min , y_max )
1618
1619 -class HwUList(histograms_PhysicsObjectList):
1620 """ A class implementing features related to a list of Hwu Histograms. """
1621
1622
1623
1624
1625 number_line_colors_defined = 8
1626
1628 """Test wether specified object is of the right type for this list."""
1629
1630 return isinstance(obj, HwU) or isinstance(obj, HwUList)
1631
1632 - def __init__(self, file_path, weight_header=None, run_id=None,
1633 merging_scale=None, accepted_types_order=[], consider_reweights='ALL',
1634 raw_labels=False, **opts):
1635 """ Read one plot from a file_path or a stream.
1636 This constructor reads all plots specified in target file.
1637 File_path can be a path or a stream in the argument.
1638 The option weight_header specifies an ordered list of weight names
1639 to appear in the file or stream specified. It accepted_types_order is
1640 empty, no filter is applied, otherwise only histograms of the specified
1641 types will be kept, and in this specified order for a given identical
1642 title. The option 'consider_reweights' selects whether one wants to
1643 include all the extra scale/pdf/merging variation weights. Possible values
1644 are 'ALL' or a list of the return types of the function get_HwU_wgt_label_type().
1645 The option 'raw_labels' specifies that one wants to import the
1646 histogram data with no treatment of the weight labels at all
1647 (this is used for the matplotlib output).
1648 """
1649
1650 if isinstance(file_path, str):
1651 stream = open(file_path,'r')
1652 elif isinstance(file_path, file):
1653 stream = file_path
1654 else:
1655 return super(HwUList,self).__init__(file_path, **opts)
1656
1657 try:
1658
1659 self.parse_histos_from_PY8_XML_stream(stream, run_id,
1660 merging_scale, accepted_types_order,
1661 consider_reweights=consider_reweights,
1662 raw_labels=raw_labels)
1663 except XMLParsingError:
1664
1665 stream.seek(0)
1666
1667 if not weight_header:
1668 weight_header = HwU.parse_weight_header(stream,raw_labels=raw_labels)
1669
1670
1671 selected_label = None
1672 if not merging_scale is None:
1673 for label in weight_header:
1674 if HwU.get_HwU_wgt_label_type(label)=='merging_scale':
1675 if float(label[1])==merging_scale:
1676 selected_label = label
1677 break
1678 if selected_label is None:
1679 raise MadGraph5Error("No weight could be found in the input HwU "+\
1680 "for the selected merging scale '%4.2f'."%merging_scale)
1681
1682 new_histo = HwU(stream, weight_header,raw_labels=raw_labels,
1683 consider_reweights=consider_reweights,
1684 selected_central_weight=selected_label)
1685
1686 while not new_histo.bins is None:
1687 if accepted_types_order==[] or \
1688 new_histo.type in accepted_types_order:
1689 self.append(new_histo)
1690 new_histo = HwU(stream, weight_header, raw_labels=raw_labels,
1691 consider_reweights=consider_reweights,
1692 selected_central_weight=selected_label)
1693
1694
1695
1696
1697
1698
1699
1700 titles_order = [h.title for h in self]
1701 def ordering_function(histo):
1702 title_position = titles_order.index(histo.title)
1703 if accepted_types_order==[]:
1704 type_precedence = {'NLO':1,'LO':2,None:3,'AUX':5}
1705 try:
1706 ordering_key = (title_position,type_precedence[histo.type])
1707 except KeyError:
1708 ordering_key = (title_position,4)
1709 else:
1710 ordering_key = (title_position,
1711 accepted_types_order.index(histo.type))
1712 return ordering_key
1713
1714
1715
1716
1717
1718 self.sort(key=ordering_function)
1719
1720
1721 if isinstance(file_path, str):
1722 stream.close()
1723
1731
1733 """ return the list of all weights define in each histograms"""
1734
1735 return self[0].bins.weight_labels
1736
1737
1738 - def get(self, name):
1739 """return the HWU histograms related to a given name"""
1740 for hist in self:
1741 if hist.get_HwU_histogram_name() == name:
1742 return hist
1743
1744 raise NameError("no histogram with name: %s" % name)
1745
1746 - def parse_histos_from_PY8_XML_stream(self, stream, run_id=None,
1747 merging_scale=None, accepted_types_order=[],
1748 consider_reweights='ALL', raw_labels=False):
1749 """Initialize the HwU histograms from an XML stream. Only one run is
1750 used: the first one if run_id is None or the specified run otherwise.
1751 Accepted type order is a filter to select histograms of only a certain
1752 type. The option 'consider_reweights' selects whether one wants to
1753 include all the extra scale/pdf/merging variation weights.
1754 Possible values are 'ALL' or a list of the return types of the
1755 function get_HwU_wgt_label_type()."""
1756
1757 run_nodes = minidom.parse(stream).getElementsByTagName("run")
1758 all_nodes = dict((int(node.getAttribute('id')),node) for
1759 node in run_nodes)
1760 selected_run_node = None
1761 weight_header = None
1762 if run_id is None:
1763 if len(run_nodes)>0:
1764 selected_run_node = all_nodes[min(all_nodes.keys())]
1765 else:
1766 try:
1767 selected_run_node = all_nodes[int(run_id)]
1768 except:
1769 selected_run_node = None
1770
1771 if selected_run_node is None:
1772 if run_id is None:
1773 raise MadGraph5Error('No histogram was found in the specified XML source.')
1774 else:
1775 raise MadGraph5Error("Histogram with run_id '%d' was not found in the "%run_id+\
1776 "specified XML source.")
1777
1778
1779
1780 if raw_labels:
1781
1782 weight_label_list = [wgt.strip() for wgt in
1783 str(selected_run_node.getAttribute('header')).split(';') if
1784 not re.match('^\s*$',wgt)]
1785 ordered_weight_label_list = [w for w in weight_label_list if w not\
1786 in ['xmin','xmax']]
1787
1788 filtered_ordered_weight_label_list = []
1789 for wgt_label in ordered_weight_label_list:
1790 if wgt_label not in filtered_ordered_weight_label_list:
1791 filtered_ordered_weight_label_list.append(wgt_label)
1792
1793 selected_weights = dict([ (wgt_pos,
1794 [wgt if wgt not in ['xmin','xmax'] else HwU.mandatory_weights[wgt]])
1795 for wgt_pos, wgt in enumerate(weight_label_list) if wgt in
1796 filtered_ordered_weight_label_list+['xmin','xmax']])
1797
1798 return self.retrieve_plots_from_XML_source(selected_run_node,
1799 selected_weights, filtered_ordered_weight_label_list,
1800 raw_labels=True)
1801
1802
1803
1804
1805 all_weights = []
1806 for wgt_position, wgt_label in \
1807 enumerate(str(selected_run_node.getAttribute('header')).split(';')):
1808 if not re.match('^\s*$',wgt_label) is None:
1809 continue
1810 all_weights.append({'POSITION':wgt_position})
1811 for wgt_item in wgt_label.strip().split('_'):
1812 property = wgt_item.strip().split('=')
1813 if len(property) == 2:
1814 all_weights[-1][property[0].strip()] = property[1].strip()
1815 elif len(property)==1:
1816 all_weights[-1][property[0].strip()] = None
1817 else:
1818 raise MadGraph5Error("The weight label property %s could not be parsed."%wgt_item)
1819
1820
1821
1822
1823
1824 for wgt_label in all_weights:
1825 for mandatory_attribute in ['PDF','MUR','MUF','MERGING','ALPSFACT']:
1826 if mandatory_attribute not in wgt_label:
1827 wgt_label[mandatory_attribute] = '-1'
1828 if mandatory_attribute=='PDF':
1829 wgt_label[mandatory_attribute] = int(wgt_label[mandatory_attribute])
1830 elif mandatory_attribute in ['MUR','MUF','MERGING','ALPSFACT']:
1831 wgt_label[mandatory_attribute] = float(wgt_label[mandatory_attribute])
1832
1833
1834
1835
1836 if merging_scale is None or merging_scale < 0.0:
1837 merging_scale_chosen = all_weights[2]['MERGING']
1838 else:
1839 merging_scale_chosen = merging_scale
1840
1841
1842 central_PDF = all_weights[2]['PDF']
1843
1844 central_MUR = all_weights[2]['MUR'] if all_weights[2]['MUR']!=-1.0 else 1.0
1845 central_MUF = all_weights[2]['MUF'] if all_weights[2]['MUF']!=-1.0 else 1.0
1846 central_alpsfact = all_weights[2]['ALPSFACT'] if all_weights[2]['ALPSFACT']!=-1.0 else 1.0
1847
1848
1849
1850 selected_weights = {}
1851
1852 if 'xmin' not in all_weights[0] or \
1853 'xmax' not in all_weights[1] or \
1854 'Weight' not in all_weights[2] or \
1855 'WeightError' not in all_weights[3]:
1856 raise MadGraph5Error('The first weight entries in the XML HwU '+\
1857 ' source are not the standard expected ones (xmin, xmax, sigmaCentral, errorCentral)')
1858 selected_weights[0] = ['xmin']
1859 selected_weights[1] = ['xmax']
1860
1861
1862 def get_difference_to_central(weight):
1863 """ Return the list of properties which differ from the central weight.
1864 This disregards the merging scale value for which any central value
1865 can be picked anyway."""
1866
1867 differences = []
1868
1869
1870
1871 if 'Weight' in weight:
1872 return set([])
1873 if weight['MUR'] not in [central_MUR, -1.0] or \
1874 weight['MUF'] not in [central_MUF, -1.0]:
1875 differences.append('mur_muf_scale')
1876 if weight['PDF'] not in [central_PDF,-1]:
1877 differences.append('pdf')
1878 if weight['ALPSFACT'] not in [central_alpsfact, -1]:
1879 differences.append('ALPSFACT')
1880 return set(differences)
1881
1882 def format_weight_label(weight):
1883 """ Print the weight attributes in a nice order."""
1884
1885 all_properties = list(weight.keys())
1886 all_properties.pop(all_properties.index('POSITION'))
1887 ordered_properties = []
1888
1889 for property in all_properties:
1890 if weight[property] is None:
1891 ordered_properties.append(property)
1892
1893 ordered_properties.sort()
1894 all_properties = [property for property in all_properties if
1895 not weight[property] is None]
1896
1897
1898 for property in ['PDF','MUR','MUF','ALPSFACT','MERGING']:
1899 all_properties.pop(all_properties.index(property))
1900 if weight[property]!=-1:
1901 ordered_properties.append(property)
1902
1903 ordered_properties.extend(sorted(all_properties))
1904
1905 return '_'.join('%s%s'\
1906 %(key,'' if weight[key] is None else '=%s'%str(weight[key])) for
1907 key in ordered_properties)
1908
1909
1910
1911
1912
1913 if float(all_weights[2]['MERGING']) == merging_scale_chosen:
1914 selected_weights[2]=['central value']
1915 else:
1916 for weight_position, weight in enumerate(all_weights):
1917
1918
1919 if get_difference_to_central(weight)==set([]):
1920
1921 if weight['MERGING']==merging_scale_chosen:
1922 selected_weights[weight_position] = ['central value']
1923 break
1924
1925 if 'central value' not in sum(list(selected_weights.values()),[]):
1926 central_merging_scale = all_weights[2]['MERGING']
1927 logger.warning('Could not find the central weight for the'+\
1928 ' chosen merging scale (%f).\n'%merging_scale_chosen+\
1929 'MG5aMC will chose the original central scale provided which '+\
1930 'correspond to a merging scale of %s'%("'inclusive'" if
1931 central_merging_scale in [0.0,-1.0] else '%f'%central_merging_scale))
1932 selected_weights[2]=['central value']
1933
1934
1935 selected_weights[3]=['dy']
1936
1937
1938 for weight_position, weight in enumerate(all_weights[4:]):
1939
1940
1941
1942
1943
1944
1945 variations = get_difference_to_central(weight)
1946
1947
1948
1949
1950
1951
1952
1953 if variations in [set(['mur_muf_scale']),set(['pdf','mur_muf_scale'])]:
1954 wgt_label = ('scale',weight['MUR'],weight['MUF'])
1955 if variations in [set(['ALPSFACT']),set(['pdf','ALPSFACT'])]:
1956 wgt_label = ('alpsfact',weight['ALPSFACT'])
1957 if variations == set(['pdf']):
1958 wgt_label = ('pdf',weight['PDF'])
1959 if variations == set([]):
1960
1961 wgt_label = format_weight_label(weight)
1962
1963
1964 if weight['MERGING'] != merging_scale_chosen:
1965
1966 if merging_scale:
1967 continue
1968
1969
1970 if variations == set([]):
1971
1972 wgt_label = ('merging_scale', weight['MERGING'])
1973
1974
1975
1976 if wgt_label in sum(list(selected_weights.values()),[]):
1977 continue
1978
1979
1980 try:
1981 selected_weights[weight_position+4].append(wgt_label)
1982 except KeyError:
1983 selected_weights[weight_position+4]=[wgt_label,]
1984
1985 if merging_scale and merging_scale > 0.0 and \
1986 len(sum(list(selected_weights.values()),[]))==4:
1987 logger.warning('No additional variation weight was found for the '+\
1988 'chosen merging scale %f.'%merging_scale)
1989
1990
1991 for wgt_pos in selected_weights:
1992 for i, weight_label in enumerate(selected_weights[wgt_pos]):
1993 try:
1994 selected_weights[wgt_pos][i] = HwU.mandatory_weights[weight_label]
1995 except KeyError:
1996 pass
1997
1998
1999 if consider_reweights!='ALL':
2000 new_selected_weights = {}
2001 for wgt_position, wgt_labels in selected_weights.items():
2002 for wgt_label in wgt_labels:
2003 if wgt_label in ['central','stat_error','boundary_xmin','boundary_xmax'] or\
2004 HwU.get_HwU_wgt_label_type(wgt_label) in consider_reweights:
2005 try:
2006 new_selected_weights[wgt_position].append(wgt_label)
2007 except KeyError:
2008 new_selected_weights[wgt_position] = [wgt_label]
2009 selected_weights = new_selected_weights
2010
2011
2012 weight_label_list = sum(list(selected_weights.values()),[])
2013
2014
2015 ordered_weight_label_list = ['central','stat_error']
2016 for weight_label in weight_label_list:
2017 if not isinstance(weight_label, str):
2018 ordered_weight_label_list.append(weight_label)
2019 for weight_label in weight_label_list:
2020 if weight_label in ['central','stat_error','boundary_xmin','boundary_xmax']:
2021 continue
2022 if isinstance(weight_label, str):
2023 ordered_weight_label_list.append(weight_label)
2024
2025
2026
2027 return self.retrieve_plots_from_XML_source(selected_run_node,
2028 selected_weights, ordered_weight_label_list, raw_labels=False)
2029
2032 """Given an XML node and the selected weights and their ordered list,
2033 import all histograms from the specified XML node."""
2034
2035
2036 for multiplicity_node in xml_node.getElementsByTagName("jethistograms"):
2037 multiplicity = int(multiplicity_node.getAttribute('njet'))
2038 for histogram in multiplicity_node.getElementsByTagName("histogram"):
2039
2040 if histogram.getAttribute("weight")!='all':
2041 continue
2042 new_histo = HwU()
2043 hist_name = '%s %s'%(str(histogram.getAttribute('name')),
2044 str(histogram.getAttribute('unit')))
2045
2046 new_histo.process_histogram_name('%s |JETSAMPLE@%d'%(hist_name,multiplicity))
2047
2048
2049 if new_histo.type == 'AUX':
2050 continue
2051
2052
2053
2054 new_histo.bins = BinList(weight_labels = ordered_weight_label_list)
2055 hist_data = str(histogram.childNodes[0].data)
2056 for line in hist_data.split('\n'):
2057 if line.strip()=='':
2058 continue
2059 bin_weights = {}
2060 boundaries = [0.0,0.0]
2061 for j, weight in \
2062 enumerate(HwU.histo_bin_weight_re.finditer(line)):
2063 try:
2064 for wgt_label in selected_weights[j]:
2065 if wgt_label == 'boundary_xmin':
2066 boundaries[0] = float(weight.group('weight'))
2067 elif wgt_label == 'boundary_xmax':
2068 boundaries[1] = float(weight.group('weight'))
2069 else:
2070 if weight.group('weight').upper()=='NAN':
2071 raise MadGraph5Error("Some weights are found to be 'NAN' in histogram with name '%s'"%hist_name+\
2072 " and jet sample multiplicity %d."%multiplicity)
2073 else:
2074 bin_weights[wgt_label] = \
2075 float(weight.group('weight'))
2076 except KeyError:
2077 continue
2078
2079 if len(bin_weights)!=len(ordered_weight_label_list):
2080 raise MadGraph5Error('Not all defined weights were found in the XML source.\n'+\
2081 '%d found / %d expected.'%(len(bin_weights),len(ordered_weight_label_list))+\
2082 '\nThe missing ones are: %s.'%\
2083 str(list(set(ordered_weight_label_list)-set(bin_weights.keys())))+\
2084 "\nIn plot with title '%s' and jet sample multiplicity %d."%\
2085 (hist_name, multiplicity))
2086
2087 new_histo.bins.append(Bin(tuple(boundaries), bin_weights))
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105 if not raw_labels:
2106 new_histo.trim_auxiliary_weights()
2107
2108
2109 self.append(new_histo)
2110
2111 - def output(self, path, format='gnuplot',number_of_ratios = -1,
2112 uncertainties=['scale','pdf','statitistical','merging_scale','alpsfact'],
2113 use_band = None,
2114 ratio_correlations=True, arg_string='',
2115 jet_samples_to_keep=None,
2116 auto_open=True,
2117 lhapdfconfig='lhapdf-config',
2118 assigned_colours=None):
2119 """ Ouput this histogram to a file, stream or string if path is kept to
2120 None. The supported format are for now. Chose whether to print the header
2121 or not."""
2122
2123 if len(self)==0:
2124 return MadGraph5Error, 'No histograms stored in the list yet.'
2125
2126 if not format in HwU.output_formats_implemented:
2127 raise MadGraph5Error("The specified output format '%s'"%format+\
2128 " is not yet supported. Supported formats are %s."\
2129 %HwU.output_formats_implemented)
2130
2131 if isinstance(path, str) and not any(ext in os.path.basename(path) \
2132 for ext in ['.Hwu','.ps','.gnuplot','.pdf']):
2133 output_base_name = os.path.basename(path)
2134 HwU_stream = open(path+'.HwU','w')
2135 else:
2136 raise MadGraph5Error("The path argument of the output function of"+\
2137 " the HwUList instance must be file path without its extension.")
2138
2139 HwU_output_list = []
2140
2141
2142 if format == 'HwU':
2143 HwU_output_list.extend(self[0].get_HwU_source(print_header=True))
2144 for histo in self[1:]:
2145 HwU_output_list.extend(histo.get_HwU_source())
2146 HwU_output_list.extend(['',''])
2147 HwU_stream.write('\n'.join(HwU_output_list))
2148 HwU_stream.close()
2149 return
2150
2151
2152 if format == 'gnuplot':
2153 gnuplot_stream = open(path+'.gnuplot','w')
2154
2155
2156 matching_histo_lists = HwUList([HwUList([self[0]])])
2157 for histo in self[1:]:
2158 matched = False
2159 for histo_list in matching_histo_lists:
2160 if histo.test_plot_compability(histo_list[0],
2161 consider_type=False, consider_unknown_weight_labels=True):
2162 histo_list.append(histo)
2163 matched = True
2164 break
2165 if not matched:
2166 matching_histo_lists.append(HwUList([histo]))
2167
2168 self[:] = matching_histo_lists
2169
2170
2171 coli=['col1','col2','col3','col4','col5','col6','col7','col8']
2172 colours={coli[0] : "#009e73",
2173 coli[1] : "#0072b2",
2174 coli[2] : "#d55e00",
2175 coli[3] : "#f0e442",
2176 coli[4] : "#56b4e9",
2177 coli[5] : "#cc79a7",
2178 coli[6] : "#e69f00",
2179 coli[7] : "black"}
2180 if assigned_colours:
2181 for index, item in enumerate(assigned_colours):
2182 if (item != None): colours[coli[index]]=item
2183
2184 replace_dict=colours
2185 replace_dict['arg_string']=arg_string
2186 replace_dict['output_base_name']=output_base_name
2187
2188
2189 gnuplot_output_list_v4 = [
2190 """
2191 ################################################################################
2192 #
2193 # This gnuplot file was generated by MadGraph5_aMC@NLO project, a program which
2194 # automatically generates Feynman diagrams and matrix elements for arbitrary
2195 # high-energy processes in the Standard Model and beyond. It also perform the
2196 # integration and/or generate events for these processes, at LO and NLO accuracy.
2197 #
2198 # For more information, visit madgraph.phys.ucl.ac.be and amcatnlo.web.cern.ch
2199 #
2200 ################################################################################
2201 # %(arg_string)s
2202 reset
2203
2204 set lmargin 10
2205 set rmargin 0
2206 set terminal postscript portrait enhanced mono dashed lw 1.0 "Helvetica" 9
2207 # The pdf terminal offers transparency support, but you will have to adapt things a bit
2208 #set terminal pdf enhanced font "Helvetica 12" lw 1.0 dashed size 29.7cm, 21cm
2209 set key font ",9"
2210 set key samplen "2"
2211 set output "%(output_base_name)s.ps"
2212
2213 # This is the "PODO" color palette of gnuplot v.5, but with the order
2214 # changed: palette of colors selected to be easily distinguishable by
2215 # color-blind individuals with either protanopia or deuteranopia. Bang
2216 # Wong [2011] Nature Methods 8, 441.
2217
2218 set style line 1 lt 1 lc rgb "%(col1)s" lw 2.5
2219 set style line 11 lt 2 lc rgb "%(col1)s" lw 2.5
2220 set style line 21 lt 4 lc rgb "%(col1)s" lw 2.5
2221 set style line 31 lt 6 lc rgb "%(col1)s" lw 2.5
2222 set style line 41 lt 8 lc rgb "%(col1)s" lw 2.5
2223
2224 set style line 2 lt 1 lc rgb "%(col2)s" lw 2.5
2225 set style line 12 lt 2 lc rgb "%(col2)s" lw 2.5
2226 set style line 22 lt 4 lc rgb "%(col2)s" lw 2.5
2227 set style line 32 lt 6 lc rgb "%(col2)s" lw 2.5
2228 set style line 42 lt 8 lc rgb "%(col2)s" lw 2.5
2229
2230 set style line 3 lt 1 lc rgb "%(col3)s" lw 2.5
2231 set style line 13 lt 2 lc rgb "%(col3)s" lw 2.5
2232 set style line 23 lt 4 lc rgb "%(col3)s" lw 2.5
2233 set style line 33 lt 6 lc rgb "%(col3)s" lw 2.5
2234 set style line 43 lt 8 lc rgb "%(col3)s" lw 2.5
2235
2236 set style line 4 lt 1 lc rgb "%(col4)s" lw 2.5
2237 set style line 14 lt 2 lc rgb "%(col4)s" lw 2.5
2238 set style line 24 lt 4 lc rgb "%(col4)s" lw 2.5
2239 set style line 34 lt 6 lc rgb "%(col4)s" lw 2.5
2240 set style line 44 lt 8 lc rgb "%(col4)s" lw 2.5
2241
2242 set style line 5 lt 1 lc rgb "%(col5)s" lw 2.5
2243 set style line 15 lt 2 lc rgb "%(col5)s" lw 2.5
2244 set style line 25 lt 4 lc rgb "%(col5)s" lw 2.5
2245 set style line 35 lt 6 lc rgb "%(col5)s" lw 2.5
2246 set style line 45 lt 8 lc rgb "%(col5)s" lw 2.5
2247
2248 set style line 6 lt 1 lc rgb "%(col6)s" lw 2.5
2249 set style line 16 lt 2 lc rgb "%(col6)s" lw 2.5
2250 set style line 26 lt 4 lc rgb "%(col6)s" lw 2.5
2251 set style line 36 lt 6 lc rgb "%(col6)s" lw 2.5
2252 set style line 46 lt 8 lc rgb "%(col6)s" lw 2.5
2253
2254 set style line 7 lt 1 lc rgb "%(col7)s" lw 2.5
2255 set style line 17 lt 2 lc rgb "%(col7)s" lw 2.5
2256 set style line 27 lt 4 lc rgb "%(col7)s" lw 2.5
2257 set style line 37 lt 6 lc rgb "%(col7)s" lw 2.5
2258 set style line 47 lt 8 lc rgb "%(col7)s" lw 2.5
2259
2260 set style line 8 lt 1 lc rgb "%(col8)s" lw 2.5
2261 set style line 18 lt 2 lc rgb "%(col8)s" lw 2.5
2262 set style line 28 lt 4 lc rgb "%(col8)s" lw 2.5
2263 set style line 38 lt 6 lc rgb "%(col8)s" lw 2.5
2264 set style line 48 lt 7 lc rgb "%(col8)s" lw 2.5
2265
2266
2267 set style line 999 lt 1 lc rgb "gray" lw 2.5
2268
2269 safe(x,y,a) = (y == 0.0 ? a : x/y)
2270
2271 set style data histeps
2272 set key invert
2273
2274 """%(replace_dict)
2275 ]
2276
2277 gnuplot_output_list_v5 = [
2278 """
2279 ################################################################################
2280 #
2281 # This gnuplot file was generated by MadGraph5_aMC@NLO project, a program which
2282 # automatically generates Feynman diagrams and matrix elements for arbitrary
2283 # high-energy processes in the Standard Model and beyond. It also perform the
2284 # integration and/or generate events for these processes, at LO and NLO accuracy.
2285 #
2286 # For more information, visit madgraph.phys.ucl.ac.be and amcatnlo.web.cern.ch
2287 #
2288 ################################################################################
2289 # %(arg_string)s
2290 reset
2291
2292 set lmargin 10
2293 set rmargin 0
2294 set terminal postscript portrait enhanced color "Helvetica" 9
2295 # The pdf terminal offers transparency support, but you will have to adapt things a bit
2296 #set terminal pdf enhanced font "Helvetica 12" lw 1.0 dashed size 29.7cm, 21cm
2297 set key font ",9"
2298 set key samplen "2"
2299 set output "%(output_base_name)s.ps"
2300
2301 # This is the "PODO" color palette of gnuplot v.5, but with the order
2302 # changed: palette of colors selected to be easily distinguishable by
2303 # color-blind individuals with either protanopia or deuteranopia. Bang
2304 # Wong [2011] Nature Methods 8, 441.
2305
2306 set style line 1 lt 1 lc rgb "%(col1)s" lw 1.3
2307 set style line 101 lt 1 lc rgb "%(col1)s" lw 1.3 dt (6,3)
2308 set style line 11 lt 2 lc rgb "%(col1)s" lw 1.3 dt (6,3)
2309 set style line 21 lt 4 lc rgb "%(col1)s" lw 1.3 dt (3,2)
2310 set style line 31 lt 6 lc rgb "%(col1)s" lw 1.3 dt (2,1)
2311 set style line 41 lt 8 lc rgb "%(col1)s" lw 1.3 dt (4,3)
2312
2313 set style line 2 lt 1 lc rgb "%(col2)s" lw 1.3
2314 set style line 102 lt 1 lc rgb "%(col2)s" lw 1.3 dt (6,3)
2315 set style line 12 lt 2 lc rgb "%(col2)s" lw 1.3 dt (6,3)
2316 set style line 22 lt 4 lc rgb "%(col2)s" lw 1.3 dt (3,2)
2317 set style line 32 lt 6 lc rgb "%(col2)s" lw 1.3 dt (2,1)
2318 set style line 42 lt 8 lc rgb "%(col2)s" lw 1.3 dt (4,3)
2319
2320 set style line 3 lt 1 lc rgb "%(col3)s" lw 1.3
2321 set style line 103 lt 1 lc rgb "%(col3)s" lw 1.3 dt (6,3)
2322 set style line 13 lt 2 lc rgb "%(col3)s" lw 1.3 dt (6,3)
2323 set style line 23 lt 4 lc rgb "%(col3)s" lw 1.3 dt (3,2)
2324 set style line 33 lt 6 lc rgb "%(col3)s" lw 1.3 dt (2,1)
2325 set style line 43 lt 8 lc rgb "%(col3)s" lw 1.3 dt (4,3)
2326
2327 set style line 4 lt 1 lc rgb "%(col4)s" lw 1.3
2328 set style line 104 lt 1 lc rgb "%(col4)s" lw 1.3 dt (6,3)
2329 set style line 14 lt 2 lc rgb "%(col4)s" lw 1.3 dt (6,3)
2330 set style line 24 lt 4 lc rgb "%(col4)s" lw 1.3 dt (3,2)
2331 set style line 34 lt 6 lc rgb "%(col4)s" lw 1.3 dt (2,1)
2332 set style line 44 lt 8 lc rgb "%(col4)s" lw 1.3 dt (4,3)
2333
2334 set style line 5 lt 1 lc rgb "%(col5)s" lw 1.3
2335 set style line 105 lt 1 lc rgb "%(col5)s" lw 1.3 dt (6,3)
2336 set style line 15 lt 2 lc rgb "%(col5)s" lw 1.3 dt (6,3)
2337 set style line 25 lt 4 lc rgb "%(col5)s" lw 1.3 dt (3,2)
2338 set style line 35 lt 6 lc rgb "%(col5)s" lw 1.3 dt (2,1)
2339 set style line 45 lt 8 lc rgb "%(col5)s" lw 1.3 dt (4,3)
2340
2341 set style line 6 lt 1 lc rgb "%(col6)s" lw 1.3
2342 set style line 106 lt 1 lc rgb "%(col6)s" lw 1.3 dt (6,3)
2343 set style line 16 lt 2 lc rgb "%(col6)s" lw 1.3 dt (6,3)
2344 set style line 26 lt 4 lc rgb "%(col6)s" lw 1.3 dt (3,2)
2345 set style line 36 lt 6 lc rgb "%(col6)s" lw 1.3 dt (2,1)
2346 set style line 46 lt 8 lc rgb "%(col6)s" lw 1.3 dt (4,3)
2347
2348 set style line 7 lt 1 lc rgb "%(col7)s" lw 1.3
2349 set style line 107 lt 1 lc rgb "%(col7)s" lw 1.3 dt (6,3)
2350 set style line 17 lt 2 lc rgb "%(col7)s" lw 1.3 dt (6,3)
2351 set style line 27 lt 4 lc rgb "%(col7)s" lw 1.3 dt (3,2)
2352 set style line 37 lt 6 lc rgb "%(col7)s" lw 1.3 dt (2,1)
2353 set style line 47 lt 8 lc rgb "%(col7)s" lw 1.3 dt (4,3)
2354
2355 set style line 8 lt 1 lc rgb "%(col8)s" lw 1.3
2356 set style line 108 lt 1 lc rgb "%(col8)s" lw 1.3 dt (6,3)
2357 set style line 18 lt 2 lc rgb "%(col8)s" lw 1.3 dt (6,3)
2358 set style line 28 lt 4 lc rgb "%(col8)s" lw 1.3 dt (3,2)
2359 set style line 38 lt 6 lc rgb "%(col8)s" lw 1.3 dt (2,1)
2360 set style line 48 lt 8 lc rgb "%(col8)s" lw 1.3 dt (4,3)
2361
2362
2363 set style line 999 lt 1 lc rgb "gray" lw 1.3
2364
2365 safe(x,y,a) = (y == 0.0 ? a : x/y)
2366
2367 set style data histeps
2368 set key invert
2369
2370 """%(replace_dict)
2371 ]
2372
2373
2374 try:
2375 p = subprocess.Popen(['gnuplot', '--version'], \
2376 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
2377 except OSError:
2378
2379
2380 gnuplot_output_list=gnuplot_output_list_v5
2381 else:
2382 output, _ = p.communicate()
2383 output.decode()
2384 if not output:
2385 gnuplot_output_list=gnuplot_output_list_v5
2386 elif float(output.split()[1]) < 5. :
2387 gnuplot_output_list=gnuplot_output_list_v4
2388 else:
2389 gnuplot_output_list=gnuplot_output_list_v5
2390
2391
2392
2393
2394 block_position = 0
2395 for histo_group in self:
2396
2397 block_position = histo_group.output_group(HwU_output_list,
2398 gnuplot_output_list, block_position,output_base_name+'.HwU',
2399 number_of_ratios=number_of_ratios,
2400 uncertainties = uncertainties,
2401 use_band = use_band,
2402 ratio_correlations = ratio_correlations,
2403 jet_samples_to_keep=jet_samples_to_keep,
2404 lhapdfconfig = lhapdfconfig)
2405
2406
2407 gnuplot_output_list.extend([
2408 "unset multiplot",
2409 '!ps2pdf "%s.ps" &> /dev/null'%output_base_name])
2410 if auto_open:
2411 gnuplot_output_list.append(
2412 '!open "%s.pdf" &> /dev/null'%output_base_name)
2413
2414
2415 gnuplot_stream.write('\n'.join(gnuplot_output_list))
2416 HwU_stream.write('\n'.join(HwU_output_list))
2417 gnuplot_stream.close()
2418 HwU_stream.close()
2419
2420 logger.debug("Histograms have been written out at "+\
2421 "%s.[HwU|gnuplot]' and can "%output_base_name+\
2422 "now be rendered by invoking gnuplot.")
2423
2424 - def output_group(self, HwU_out, gnuplot_out, block_position, HwU_name,
2425 number_of_ratios = -1,
2426 uncertainties = ['scale','pdf','statitistical','merging_scale','alpsfact'],
2427 use_band = None,
2428 ratio_correlations = True,
2429 jet_samples_to_keep=None,
2430 lhapdfconfig='lhapdf-config'):
2431
2432 """ This functions output a single group of histograms with either one
2433 histograms untyped (i.e. type=None) or two of type 'NLO' and 'LO'
2434 respectively."""
2435
2436
2437
2438 def get_main_central_plot_lines(HwU_name, block_position, color_index,
2439 title, show_mc_uncertainties):
2440 """ Returns two plot lines, one for the negative contributions in
2441 dashed and one with the positive ones in solid."""
2442
2443 template = "'%(hwu)s' index %(ind)d using (($1+$2)/2):%(data)s%(stat_col)s%(stat_err)s%(ls)s%(title)s"
2444 template_no_stat = "'%(hwu)s' index %(ind)d using (($1+$2)/2):%(data)s%(ls)s%(title)s"
2445 rep_dic = {'hwu':HwU_name,
2446 'ind':block_position,
2447 'ls':' ls %d'%color_index,
2448 'title':" title '%s'"%title,
2449 'stat_col': ':4',
2450 'stat_err': ' w yerrorbar',
2451 'data':'3',
2452 'linetype':''}
2453
2454
2455
2456
2457
2458
2459 res = []
2460 rep_dic['data'] = '($3 < 0 ? sqrt(-1) : $3)'
2461 res.append(template_no_stat%rep_dic)
2462 rep_dic['title'] = " title ''"
2463 if show_mc_uncertainties:
2464 res.append(template%rep_dic)
2465 rep_dic['data'] = '($3 >= 0 ? sqrt(-1) : abs($3))'
2466 rep_dic['ls'] = ' ls %d'%(100+color_index)
2467 res.append(template_no_stat%rep_dic)
2468 if show_mc_uncertainties:
2469 res.append(template%rep_dic)
2470 return res
2471
2472
2473
2474
2475 def get_uncertainty_lines(HwU_name, block_position,
2476 var_pos, color_index,title, ratio=False, band=False):
2477 """ Return a string line corresponding to the plotting of the
2478 uncertainty. Band is to chose wether to display uncertainty with
2479 a band or two lines."""
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499 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'"
2500
2501
2502 copy_swap_re = copy_swap_re.replace('\\','\\\\')
2503
2504 position = '(safe($%d,$3,1.0)-1.0)' if ratio else '%d'
2505 if not band:
2506 return ["'%s' index %d using (($1+$2)/2):%s ls %d title '%s'"\
2507 %(HwU_name,block_position, position%(var_pos),color_index,title),
2508 "'%s' index %d using (($1+$2)/2):%s ls %d title ''"\
2509 %(HwU_name,block_position, position%(var_pos+1),color_index)]
2510 else:
2511 return [' "<%s %s" index %d using 1:%s:%s with filledcurve ls %d fs transparent solid 0.2 title \'%s\''%\
2512 (copy_swap_re,HwU_name,block_position,
2513 position%var_pos,position%(var_pos+1),color_index,title)]
2514
2515
2516
2517 layout_geometry = [(0.0, 0.5, 1.0, 0.4 ),
2518 (0.0, 0.35, 1.0, 0.15),
2519 (0.0, 0.2, 1.0, 0.15)]
2520 layout_geometry.reverse()
2521
2522
2523
2524 matching_histo_lists = HwUList([HwUList([self[0]])])
2525 for histo in self[1:]:
2526 matched = False
2527 for histo_list in matching_histo_lists:
2528 if hasattr(histo, 'jetsample') and histo.jetsample >= 0 and \
2529 histo.type == histo_list[0].type:
2530 matched = True
2531 histo_list.append(histo)
2532 break
2533 if not matched:
2534 matching_histo_lists.append(HwUList([histo]))
2535
2536
2537
2538 self[:] = []
2539 for histo_group in matching_histo_lists:
2540
2541
2542 if len(histo_group)==1:
2543 self.append(histo_group[0])
2544 continue
2545
2546
2547 if any(hist.jetsample==-1 for hist in histo_group if
2548 hasattr(hist, 'jetsample')):
2549 self.extend(histo_group)
2550 continue
2551 summed_histogram = copy.copy(histo_group[0])
2552 for histo in histo_group[1:]:
2553 summed_histogram = summed_histogram + histo
2554 summed_histogram.jetsample = -1
2555 self.append(summed_histogram)
2556 self.extend(histo_group)
2557
2558
2559 if not jet_samples_to_keep is None:
2560 self[:] = [histo for histo in self if (not hasattr(histo,'jetsample')) or (histo.jetsample == -1) or
2561 (histo.jetsample in jet_samples_to_keep)]
2562
2563
2564
2565 def ratio_no_correlations(wgtsA, wgtsB):
2566 new_wgts = {}
2567 for label, wgt in wgtsA.items():
2568 if wgtsB['central']==0.0 and wgt==0.0:
2569 new_wgts[label] = 0.0
2570 continue
2571 elif wgtsB['central']==0.0:
2572
2573
2574
2575 new_wgts[label] = 0.0
2576 continue
2577 new_wgts[label] = (wgtsA[label]/wgtsB['central'])
2578 return new_wgts
2579
2580
2581
2582 n_histograms = len(self)
2583 ratio_histos = HwUList([])
2584
2585 n_ratios_included = 0
2586 for i, histo in enumerate(self[1:]):
2587 if not hasattr(histo,'jetsample') or histo.jetsample==self[0].jetsample:
2588 n_ratios_included += 1
2589 else:
2590 continue
2591
2592 if number_of_ratios >=0 and n_ratios_included > number_of_ratios:
2593 break
2594
2595 if ratio_correlations:
2596 ratio_histos.append(histo/self[0])
2597 else:
2598 ratio_histos.append(self[0].__class__.combine(histo, self[0],
2599 ratio_no_correlations))
2600 if self[0].type=='NLO' and self[1].type=='LO':
2601 ratio_histos[-1].title += '1/K-factor'
2602 elif self[0].type=='LO' and self[1].type=='NLO':
2603 ratio_histos[-1].title += 'K-factor'
2604 else:
2605 ratio_histos[-1].title += ' %s/%s'%(
2606 self[1].type if self[1].type else '(%d)'%(i+2),
2607 self[0].type if self[0].type else '(1)')
2608
2609
2610 ratio_histos[-1].type = 'AUX'
2611 self.extend(ratio_histos)
2612
2613
2614 if 'scale' in uncertainties:
2615 (mu_var_pos,mu) = self[0].set_uncertainty(type='all_scale')
2616 else:
2617 (mu_var_pos,mu) = (None,[None])
2618
2619 if 'pdf' in uncertainties:
2620 (PDF_var_pos,pdf) = self[0].set_uncertainty(type='PDF',lhapdfconfig=lhapdfconfig)
2621 else:
2622 (PDF_var_pos,pdf) = (None,[None])
2623
2624 if 'merging_scale' in uncertainties:
2625 (merging_var_pos,merging) = self[0].set_uncertainty(type='merging')
2626 else:
2627 (merging_var_pos,merging) = (None,[None])
2628 if 'alpsfact' in uncertainties:
2629 (alpsfact_var_pos,alpsfact) = self[0].set_uncertainty(type='alpsfact')
2630 else:
2631 (alpsfact_var_pos,alpsfact) = (None,[None])
2632
2633 uncertainties_present = list(uncertainties)
2634 if PDF_var_pos is None and 'pdf' in uncertainties_present:
2635 uncertainties_present.remove('pdf')
2636 if mu_var_pos is None and 'scale' in uncertainties_present:
2637 uncertainties_present.remove('scale')
2638 if merging_var_pos is None and 'merging' in uncertainties_present:
2639 uncertainties_present.remove('merging')
2640 if alpsfact_var_pos is None and 'alpsfact' in uncertainties_present:
2641 uncertainties_present.remove('alpsfact')
2642 no_uncertainties = len(uncertainties_present)==0
2643
2644
2645 try:
2646 uncertainties_present.remove('statistical')
2647 except:
2648 pass
2649 if use_band is None:
2650
2651
2652 if len(uncertainties_present)==0:
2653 use_band = []
2654 elif len(uncertainties_present)==1:
2655 use_band = uncertainties_present
2656 elif 'scale' in uncertainties_present:
2657 use_band = ['scale']
2658 else:
2659 use_band = [uncertainties_present[0]]
2660
2661 for histo in self[1:]:
2662 if (not mu_var_pos is None) and \
2663 mu_var_pos != histo.set_uncertainty(type='all_scale')[0]:
2664 raise MadGraph5Error('Not all histograms in this group specify'+\
2665 ' scale uncertainties. It is required to be able to output them'+\
2666 ' together.')
2667 if (not PDF_var_pos is None) and\
2668 PDF_var_pos != histo.set_uncertainty(type='PDF',\
2669 lhapdfconfig=lhapdfconfig)[0]:
2670 raise MadGraph5Error('Not all histograms in this group specify'+\
2671 ' PDF uncertainties. It is required to be able to output them'+\
2672 ' together.')
2673 if (not merging_var_pos is None) and\
2674 merging_var_pos != histo.set_uncertainty(type='merging')[0]:
2675 raise MadGraph5Error('Not all histograms in this group specify'+\
2676 ' merging uncertainties. It is required to be able to output them'+\
2677 ' together.')
2678 if (not alpsfact_var_pos is None) and\
2679 alpsfact_var_pos != histo.set_uncertainty(type='alpsfact')[0]:
2680 raise MadGraph5Error('Not all histograms in this group specify'+\
2681 ' alpsfact uncertainties. It is required to be able to output them'+\
2682 ' together.')
2683
2684
2685
2686 for i, histo in enumerate(self):
2687
2688 HwU_out.extend(histo.get_HwU_source(\
2689 print_header=(block_position==0 and i==0)))
2690 HwU_out.extend(['',''])
2691
2692
2693 global_header =\
2694 """
2695 ################################################################################
2696 ### Rendering of the plot titled '%(title)s'
2697 ################################################################################
2698
2699 set multiplot
2700 set label "%(title)s" font ",13" at graph 0.04, graph 1.05
2701 set xrange [%(xmin).4e:%(xmax).4e]
2702 set bmargin 0
2703 set tmargin 0
2704 set xtics nomirror
2705 set ytics nomirror
2706 set mytics %(mxtics)d
2707 %(set_xtics)s
2708 set key horizontal noreverse maxcols 1 width -4
2709 set label front 'MadGraph5\_aMC\@NLO' font "Courier,11" rotate by 90 at graph 1.02, graph 0.04
2710 """
2711
2712
2713 subhistogram_header = \
2714 """#-- rendering subhistograms '%(subhistogram_type)s'
2715 %(unset label)s
2716 %(set_format_y)s
2717 set yrange [%(ymin).4e:%(ymax).4e]
2718 set origin %(origin_x).4e, %(origin_y).4e
2719 set size %(size_x).4e, %(size_y).4e
2720 set mytics %(mytics)d
2721 %(set_ytics)s
2722 %(set_format_x)s
2723 %(set_yscale)s
2724 %(set_ylabel)s
2725 %(set_histo_label)s
2726 plot \\"""
2727 replacement_dic = {}
2728
2729 replacement_dic['title'] = self[0].get_HwU_histogram_name(format='human-no_type')
2730
2731
2732 wgts_to_consider = ['central']
2733 if not mu_var_pos is None:
2734 for mu_var in mu_var_pos:
2735 wgts_to_consider.append(self[0].bins.weight_labels[mu_var])
2736 wgts_to_consider.append(self[0].bins.weight_labels[mu_var+1])
2737 wgts_to_consider.append(self[0].bins.weight_labels[mu_var+2])
2738 if not PDF_var_pos is None:
2739 for PDF_var in PDF_var_pos:
2740 wgts_to_consider.append(self[0].bins.weight_labels[PDF_var])
2741 wgts_to_consider.append(self[0].bins.weight_labels[PDF_var+1])
2742 wgts_to_consider.append(self[0].bins.weight_labels[PDF_var+2])
2743 if not merging_var_pos is None:
2744 for merging_var in merging_var_pos:
2745 wgts_to_consider.append(self[0].bins.weight_labels[merging_var])
2746 wgts_to_consider.append(self[0].bins.weight_labels[merging_var+1])
2747 wgts_to_consider.append(self[0].bins.weight_labels[merging_var+2])
2748 if not alpsfact_var_pos is None:
2749 for alpsfact_var in alpsfact_var_pos:
2750 wgts_to_consider.append(self[0].bins.weight_labels[alpsfact_var])
2751 wgts_to_consider.append(self[0].bins.weight_labels[alpsfact_var+1])
2752 wgts_to_consider.append(self[0].bins.weight_labels[alpsfact_var+2])
2753
2754 (xmin, xmax) = HwU.get_x_optimal_range(self[:2],\
2755 weight_labels = wgts_to_consider)
2756 replacement_dic['xmin'] = xmin
2757 replacement_dic['xmax'] = xmax
2758 replacement_dic['mxtics'] = 10
2759 replacement_dic['set_xtics'] = 'set xtics auto'
2760
2761
2762 gnuplot_out.append(global_header%replacement_dic)
2763
2764
2765 replacement_dic['subhistogram_type'] = '%s and %s results'%(
2766 str(self[0].type),str(self[1].type)) if len(self)>1 else \
2767 'single diagram output'
2768 (ymin, ymax) = HwU.get_y_optimal_range(self[:2],
2769 labels = wgts_to_consider, scale=self[0].y_axis_mode)
2770
2771
2772 if ymin< 0.0:
2773 self[0].y_axis_mode = 'LIN'
2774
2775
2776 if self[0].y_axis_mode=='LOG':
2777 ymax += 10.0 * ymax
2778 ymin -= 0.1 * ymin
2779 else:
2780 ymax += 0.3 * (ymax - ymin)
2781 ymin -= 0.3 * (ymax - ymin)
2782
2783 replacement_dic['ymin'] = ymin
2784 replacement_dic['ymax'] = ymax
2785 replacement_dic['unset label'] = ''
2786 (replacement_dic['origin_x'], replacement_dic['origin_y'],
2787 replacement_dic['size_x'], replacement_dic['size_y']) = layout_geometry.pop()
2788 replacement_dic['mytics'] = 10
2789
2790 replacement_dic['set_ytics'] = 'set ytics auto'
2791 replacement_dic['set_format_x'] = "set format x ''" if \
2792 (len(self)-n_histograms>0 or not no_uncertainties) else "set format x"
2793 replacement_dic['set_ylabel'] = 'set ylabel "{/Symbol s} per bin [pb]"'
2794 replacement_dic['set_yscale'] = "set logscale y" if \
2795 self[0].y_axis_mode=='LOG' else 'unset logscale y'
2796 replacement_dic['set_format_y'] = "set format y '10^{%T}'" if \
2797 self[0].y_axis_mode=='LOG' else 'unset format'
2798
2799 replacement_dic['set_histo_label'] = ""
2800 gnuplot_out.append(subhistogram_header%replacement_dic)
2801
2802
2803 plot_lines = []
2804 uncertainty_plot_lines = []
2805 n=-1
2806
2807 for i, histo in enumerate(self[:n_histograms]):
2808 n=n+1
2809 color_index = n%self.number_line_colors_defined+1
2810
2811 title = []
2812 if histo.type is None and not hasattr(histo, 'jetsample'):
2813 title.append('%d'%(i+1))
2814 else:
2815 if histo.type:
2816 title.append('NLO' if \
2817 histo.type.split()[0]=='NLO' else histo.type)
2818 if hasattr(histo, 'jetsample'):
2819 if histo.jetsample!=-1:
2820 title.append('jet sample %d'%histo.jetsample)
2821 else:
2822 title.append('all jet samples')
2823
2824 title = ', '.join(title)
2825
2826 if histo.type is None and not hasattr(histo, 'jetsample'):
2827 major_title = 'central value for plot (%d)'%(i+1)
2828 else:
2829 major_title = []
2830 if not histo.type is None:
2831 major_title.append(histo.type)
2832 if hasattr(histo, 'jetsample'):
2833 if histo.jetsample!=-1:
2834 major_title.append('jet sample %d'%histo.jetsample)
2835 else:
2836 major_title.append('all jet samples')
2837 else:
2838 major_title.append('central value')
2839 major_title = ', '.join(major_title)
2840
2841 if not mu[0] in ['none',None]:
2842 major_title += ', dynamical\_scale\_choice=%s'%mu[0]
2843 if not pdf[0] in ['none',None]:
2844 major_title += ', PDF=%s'%pdf[0].replace('_','\_')
2845
2846
2847
2848 if not (i!=0 and hasattr(histo,'jetsample') and histo.jetsample!=-1 and \
2849 not (jet_samples_to_keep and len(jet_samples_to_keep)==1 and
2850 jet_samples_to_keep[0] == histo.jetsample)):
2851
2852 uncertainty_plot_lines.append({})
2853
2854
2855
2856
2857
2858
2859
2860
2861 if not mu_var_pos is None and len(mu_var_pos)>0:
2862 if 'scale' in use_band:
2863 uncertainty_plot_lines[-1]['scale'] = get_uncertainty_lines(
2864 HwU_name, block_position+i, mu_var_pos[0]+4, color_index+10,
2865 '%s, scale variation'%title, band='scale' in use_band)
2866 else:
2867 uncertainty_plot_lines[-1]['scale'] = \
2868 ["sqrt(-1) ls %d title '%s'"%(color_index+10,'%s, scale variation'%title)]
2869
2870 if not PDF_var_pos is None and len(PDF_var_pos)>0:
2871 if 'pdf' in use_band:
2872 uncertainty_plot_lines[-1]['pdf'] = get_uncertainty_lines(
2873 HwU_name,block_position+i, PDF_var_pos[0]+4, color_index+20,
2874 '%s, PDF variation'%title, band='pdf' in use_band)
2875 else:
2876 uncertainty_plot_lines[-1]['pdf'] = \
2877 ["sqrt(-1) ls %d title '%s'"%(color_index+20,'%s, PDF variation'%title)]
2878
2879 if not merging_var_pos is None and len(merging_var_pos)>0:
2880 if 'merging_scale' in use_band:
2881 uncertainty_plot_lines[-1]['merging_scale'] = get_uncertainty_lines(
2882 HwU_name,block_position+i, merging_var_pos[0]+4, color_index+30,
2883 '%s, merging scale variation'%title, band='merging_scale' in use_band)
2884 else:
2885 uncertainty_plot_lines[-1]['merging_scale'] = \
2886 ["sqrt(-1) ls %d title '%s'"%(color_index+30,'%s, merging scale variation'%title)]
2887
2888 if not alpsfact_var_pos is None and len(alpsfact_var_pos)>0:
2889 if 'alpsfact' in use_band:
2890 uncertainty_plot_lines[-1]['alpsfact'] = get_uncertainty_lines(
2891 HwU_name,block_position+i, alpsfact_var_pos[0]+4, color_index+40,
2892 '%s, alpsfact variation'%title, band='alpsfact' in use_band)
2893 else:
2894 uncertainty_plot_lines[-1]['alpsfact'] = \
2895 ["sqrt(-1) ls %d title '%s'"%(color_index+40,'%s, alpsfact variation'%title)]
2896
2897
2898
2899
2900
2901
2902
2903
2904 plot_lines.extend(
2905 get_main_central_plot_lines(HwU_name, block_position+i,
2906 color_index, major_title, 'statistical' in uncertainties))
2907
2908
2909 if not mu_var_pos is None:
2910 for j,mu_var in enumerate(mu_var_pos):
2911 if j!=0:
2912 n=n+1
2913 color_index = n%self.number_line_colors_defined+1
2914 plot_lines.append(
2915 "'%s' index %d using (($1+$2)/2):%d ls %d title '%s'"\
2916 %(HwU_name,block_position+i,mu_var+3,color_index,\
2917 '%s dynamical\_scale\_choice=%s' % (title,mu[j])))
2918
2919 if not PDF_var_pos is None:
2920 for j,PDF_var in enumerate(PDF_var_pos):
2921 if j!=0:
2922 n=n+1
2923 color_index = n%self.number_line_colors_defined+1
2924 plot_lines.append(
2925 "'%s' index %d using (($1+$2)/2):%d ls %d title '%s'"\
2926 %(HwU_name,block_position+i,PDF_var+3,color_index,\
2927 '%s PDF=%s' % (title,pdf[j].replace('_','\_'))))
2928
2929
2930
2931 for one_plot in uncertainty_plot_lines:
2932 for uncertainty_type, lines in one_plot.items():
2933 if not uncertainty_type in use_band:
2934 plot_lines.extend(lines)
2935
2936 for one_plot in uncertainty_plot_lines:
2937 for uncertainty_type, lines in one_plot.items():
2938 if uncertainty_type in use_band:
2939 plot_lines.extend(lines)
2940
2941
2942 plot_lines.reverse()
2943
2944
2945 gnuplot_out.append(',\\\n'.join(plot_lines))
2946
2947
2948 replacement_dic['subhistogram_type'] = 'Relative scale and PDF uncertainty'
2949
2950 if 'statistical' in uncertainties:
2951 wgts_to_consider.append('stat_error')
2952
2953
2954
2955 def rel_scale(wgtsA, wgtsB):
2956 new_wgts = {}
2957 for label, wgt in wgtsA.items():
2958 if label in wgts_to_consider:
2959 if wgtsB['central']==0.0 and wgt==0.0:
2960 new_wgts[label] = 0.0
2961 continue
2962 elif wgtsB['central']==0.0:
2963
2964
2965
2966 new_wgts[label] = 0.0
2967 continue
2968 new_wgts[label] = (wgtsA[label]/wgtsB['central'])
2969 if label != 'stat_error':
2970 new_wgts[label] -= 1.0
2971 else:
2972 new_wgts[label] = wgtsA[label]
2973 return new_wgts
2974
2975 histos_for_subplots = [(i,histo) for i, histo in enumerate(self[:n_histograms]) if
2976 ( not (i!=0 and hasattr(histo,'jetsample') and histo.jetsample!=-1 and \
2977 not (jet_samples_to_keep and len(jet_samples_to_keep)==1 and
2978 jet_samples_to_keep[0] == histo.jetsample)) )]
2979
2980
2981
2982
2983 (ymin, ymax) = HwU.get_y_optimal_range([histo[1].__class__.combine(
2984 histo[1],histo[1],rel_scale) for histo in histos_for_subplots],
2985 labels = wgts_to_consider, scale='LIN')
2986
2987
2988 ymax = ymax + 0.2 * (ymax - ymin)
2989 ymin = ymin - 0.2 * (ymax - ymin)
2990 replacement_dic['unset label'] = 'unset label'
2991 replacement_dic['ymin'] = ymin
2992 replacement_dic['ymax'] = ymax
2993 if not no_uncertainties:
2994 (replacement_dic['origin_x'], replacement_dic['origin_y'],
2995 replacement_dic['size_x'], replacement_dic['size_y']) = layout_geometry.pop()
2996 replacement_dic['mytics'] = 2
2997
2998 replacement_dic['set_ytics'] = 'set ytics auto'
2999 replacement_dic['set_format_x'] = "set format x ''" if \
3000 len(self)-n_histograms>0 else "set format x"
3001 replacement_dic['set_ylabel'] = 'set ylabel "%s rel.unc."'\
3002 %('(1)' if self[0].type==None else '%s'%('NLO' if \
3003 self[0].type.split()[0]=='NLO' else self[0].type))
3004 replacement_dic['set_yscale'] = "unset logscale y"
3005 replacement_dic['set_format_y'] = 'unset format'
3006
3007
3008 tit='Relative uncertainties w.r.t. central value'
3009 if n_histograms > 1:
3010 tit=tit+'s'
3011
3012
3013
3014
3015 replacement_dic['set_histo_label'] = \
3016 'set label "%s" font ",9" front at graph 0.03, graph 0.13' % tit
3017
3018
3019 if not no_uncertainties:
3020 gnuplot_out.append(subhistogram_header%replacement_dic)
3021
3022
3023 plot_lines = []
3024 uncertainty_plot_lines = []
3025 n=-1
3026 for (i,histo) in histos_for_subplots:
3027 n=n+1
3028 k=n
3029 color_index = n%self.number_line_colors_defined+1
3030
3031 if not mu_var_pos is None:
3032 for j,mu_var in enumerate(mu_var_pos):
3033 uncertainty_plot_lines.append({})
3034 if j==0:
3035 color_index = k%self.number_line_colors_defined+1
3036 else:
3037 n=n+1
3038 color_index = n%self.number_line_colors_defined+1
3039
3040 if j>0 or mu[j]!='none':
3041 plot_lines.append(
3042 "'%s' index %d using (($1+$2)/2):(safe($%d,$3,1.0)-1.0) ls %d title ''"\
3043 %(HwU_name,block_position+i,mu_var+3,color_index))
3044 uncertainty_plot_lines[-1]['scale'] = get_uncertainty_lines(
3045 HwU_name, block_position+i, mu_var+4, color_index+10,'',
3046 ratio=True, band='scale' in use_band)
3047 if not PDF_var_pos is None:
3048 for j,PDF_var in enumerate(PDF_var_pos):
3049 uncertainty_plot_lines.append({})
3050 if j==0:
3051 color_index = k%self.number_line_colors_defined+1
3052 else:
3053 n=n+1
3054 color_index = n%self.number_line_colors_defined+1
3055
3056 if j>0 or pdf[j]!='none':
3057 plot_lines.append(
3058 "'%s' index %d using (($1+$2)/2):(safe($%d,$3,1.0)-1.0) ls %d title ''"\
3059 %(HwU_name,block_position+i,PDF_var+3,color_index))
3060 uncertainty_plot_lines[-1]['pdf'] = get_uncertainty_lines(
3061 HwU_name, block_position+i, PDF_var+4, color_index+20,'',
3062 ratio=True, band='pdf' in use_band)
3063 if not merging_var_pos is None:
3064 for j,merging_var in enumerate(merging_var_pos):
3065 uncertainty_plot_lines.append({})
3066 if j==0:
3067 color_index = k%self.number_line_colors_defined+1
3068 else:
3069 n=n+1
3070 color_index = n%self.number_line_colors_defined+1
3071 if j>0 or merging[j]!='none':
3072 plot_lines.append(
3073 "'%s' index %d using (($1+$2)/2):(safe($%d,$3,1.0)-1.0) ls %d title ''"\
3074 %(HwU_name,block_position+i,merging_var+3,color_index))
3075 uncertainty_plot_lines[-1]['merging_scale'] = get_uncertainty_lines(
3076 HwU_name, block_position+i, merging_var+4, color_index+30,'',
3077 ratio=True, band='merging_scale' in use_band)
3078 if not alpsfact_var_pos is None:
3079 for j,alpsfact_var in enumerate(alpsfact_var_pos):
3080 uncertainty_plot_lines.append({})
3081 if j==0:
3082 color_index = k%self.number_line_colors_defined+1
3083 else:
3084 n=n+1
3085 color_index = n%self.number_line_colors_defined+1
3086 if j>0 or alpsfact[j]!='none':
3087 plot_lines.append(
3088 "'%s' index %d using (($1+$2)/2):(safe($%d,$3,1.0)-1.0) ls %d title ''"\
3089 %(HwU_name,block_position+i,alpsfact_var+3,color_index))
3090 uncertainty_plot_lines[-1]['alpsfact'] = get_uncertainty_lines(
3091 HwU_name, block_position+i, alpsfact_var+4, color_index+40,'',
3092 ratio=True, band='alpsfact' in use_band)
3093
3094 if 'statistical' in uncertainties:
3095 plot_lines.append(
3096 "'%s' index %d using (($1+$2)/2):(0.0):(safe($4,$3,0.0)) w yerrorbar ls %d title ''"%\
3097 (HwU_name,block_position+i,color_index))
3098
3099 plot_lines.append("0.0 ls 999 title ''")
3100
3101
3102
3103 for one_plot in uncertainty_plot_lines:
3104 for uncertainty_type, lines in one_plot.items():
3105 if not uncertainty_type in use_band:
3106 plot_lines.extend(lines)
3107
3108 for one_plot in uncertainty_plot_lines:
3109 for uncertainty_type, lines in one_plot.items():
3110 if uncertainty_type in use_band:
3111 plot_lines.extend(lines)
3112
3113
3114 plot_lines.reverse()
3115
3116 if not no_uncertainties:
3117 gnuplot_out.append(',\\\n'.join(plot_lines))
3118
3119
3120 if len(self)-n_histograms==0:
3121
3122 gnuplot_out.extend(['','unset label','',
3123 '################################################################################'])
3124
3125 return block_position+len(self)
3126
3127
3128 ratio_name_long='('
3129 for i, histo in enumerate(self[:n_histograms]):
3130 if i==0: continue
3131 ratio_name_long+='%d'%(i+1) if histo.type is None else ('NLO' if \
3132 histo.type.split()[0]=='NLO' else histo.type)
3133 ratio_name_long+=')/'
3134 ratio_name_long+=('(1' if self[0].type==None else '(%s'%('NLO' if \
3135 self[0].type.split()[0]=='NLO' else self[0].type))+' central value)'
3136
3137 ratio_name_short = 'ratio w.r.t. '+('1' if self[0].type==None else '%s'%('NLO' if \
3138 self[0].type.split()[0]=='NLO' else self[0].type))
3139
3140 replacement_dic['subhistogram_type'] = '%s ratio'%ratio_name_long
3141 replacement_dic['set_ylabel'] = 'set ylabel "%s"'%ratio_name_short
3142
3143 (ymin, ymax) = HwU.get_y_optimal_range(self[n_histograms:],
3144 labels = wgts_to_consider, scale='LIN',Kratio = True)
3145
3146
3147 ymax = ymax + 0.2 * (ymax - ymin)
3148 ymin = ymin - 0.2 * (ymax - ymin)
3149 replacement_dic['unset label'] = 'unset label'
3150 replacement_dic['ymin'] = ymin
3151 replacement_dic['ymax'] = ymax
3152 (replacement_dic['origin_x'], replacement_dic['origin_y'],
3153 replacement_dic['size_x'], replacement_dic['size_y']) = layout_geometry.pop()
3154 replacement_dic['mytics'] = 2
3155
3156 replacement_dic['set_ytics'] = 'set ytics auto'
3157 replacement_dic['set_format_x'] = "set format x"
3158 replacement_dic['set_yscale'] = "unset logscale y"
3159 replacement_dic['set_format_y'] = 'unset format'
3160 replacement_dic['set_histo_label'] = \
3161 'set label "%s" font ",9" at graph 0.03, graph 0.13'%ratio_name_long
3162
3163 gnuplot_out.append(subhistogram_header%replacement_dic)
3164
3165 uncertainty_plot_lines = []
3166 plot_lines = []
3167
3168
3169 n=-1
3170 n=n+1
3171 if not mu_var_pos is None:
3172 for j,mu_var in enumerate(mu_var_pos):
3173 if j!=0: n=n+1
3174 if not PDF_var_pos is None:
3175 for j,PDF_var in enumerate(PDF_var_pos):
3176 if j!=0: n=n+1
3177 if not merging_var_pos is None:
3178 for j,merging_var in enumerate(merging_var_pos):
3179 if j!=0: n=n+1
3180 if not alpsfact_var_pos is None:
3181 for j,alpsfact_var in enumerate(alpsfact_var_pos):
3182 if j!=0: n=n+1
3183
3184 for i_histo_ratio, histo_ration in enumerate(self[n_histograms:]):
3185 n=n+1
3186 k=n
3187 block_ratio_pos = block_position+n_histograms+i_histo_ratio
3188 color_index = n%self.number_line_colors_defined+1
3189
3190 plot_lines.append(
3191 "'%s' index %d using (($1+$2)/2):3 ls %d title ''"%\
3192 (HwU_name,block_ratio_pos,color_index))
3193 if 'statistical' in uncertainties:
3194 plot_lines.append(
3195 "'%s' index %d using (($1+$2)/2):3:4 w yerrorbar ls %d title ''"%\
3196 (HwU_name,block_ratio_pos,color_index))
3197
3198
3199 if not mu_var_pos is None:
3200 for j,mu_var in enumerate(mu_var_pos):
3201 uncertainty_plot_lines.append({})
3202 if j==0:
3203 color_index = k%self.number_line_colors_defined+1
3204 else:
3205 n=n+1
3206 color_index = n%self.number_line_colors_defined+1
3207
3208 if j>0 or mu[j]!='none':
3209 plot_lines.append(
3210 "'%s' index %d using (($1+$2)/2):%d ls %d title ''"\
3211 %(HwU_name,block_ratio_pos,mu_var+3,color_index))
3212 uncertainty_plot_lines[-1]['scale'] = get_uncertainty_lines(
3213 HwU_name, block_ratio_pos, mu_var+4, color_index+10,'',
3214 band='scale' in use_band)
3215 if not PDF_var_pos is None:
3216 for j,PDF_var in enumerate(PDF_var_pos):
3217 uncertainty_plot_lines.append({})
3218 if j==0:
3219 color_index = k%self.number_line_colors_defined+1
3220 else:
3221 n=n+1
3222 color_index = n%self.number_line_colors_defined+1
3223
3224 if j>0 or pdf[j]!='none':
3225 plot_lines.append(
3226 "'%s' index %d using (($1+$2)/2):%d ls %d title ''"\
3227 %(HwU_name,block_ratio_pos,PDF_var+3,color_index))
3228 uncertainty_plot_lines[-1]['pdf'] = get_uncertainty_lines(
3229 HwU_name, block_ratio_pos, PDF_var+4, color_index+20,'',
3230 band='pdf' in use_band)
3231 if not merging_var_pos is None:
3232 for j,merging_var in enumerate(merging_var_pos):
3233 uncertainty_plot_lines.append({})
3234 if j==0:
3235 color_index = k%self.number_line_colors_defined+1
3236 else:
3237 n=n+1
3238 color_index = n%self.number_line_colors_defined+1
3239 if j>0 or merging[j]!='none':
3240 plot_lines.append(
3241 "'%s' index %d using (($1+$2)/2):%d ls %d title ''"\
3242 %(HwU_name,block_ratio_pos,merging_var+3,color_index))
3243 uncertainty_plot_lines[-1]['merging_scale'] = get_uncertainty_lines(
3244 HwU_name, block_ratio_pos, merging_var+4, color_index+30,'',
3245 band='merging_scale' in use_band)
3246 if not alpsfact_var_pos is None:
3247 for j,alpsfact_var in enumerate(alpsfact_var_pos):
3248 uncertainty_plot_lines.append({})
3249 if j==0:
3250 color_index = k%self.number_line_colors_defined+1
3251 else:
3252 n=n+1
3253 color_index = n%self.number_line_colors_defined+1
3254 if j>0 or alpsfact[j]!='none':
3255 plot_lines.append(
3256 "'%s' index %d using (($1+$2)/2):%d ls %d title ''"\
3257 %(HwU_name,block_ratio_pos,alpsfact_var+3,color_index))
3258 uncertainty_plot_lines[-1]['alpsfact'] = get_uncertainty_lines(
3259 HwU_name, block_ratio_pos, alpsfact_var+4, color_index+40,'',
3260 band='alpsfact' in use_band)
3261
3262
3263
3264 for one_plot in uncertainty_plot_lines:
3265 for uncertainty_type, lines in one_plot.items():
3266 if not uncertainty_type in use_band:
3267 plot_lines.extend(lines)
3268
3269 for one_plot in uncertainty_plot_lines:
3270 for uncertainty_type, lines in one_plot.items():
3271 if uncertainty_type in use_band:
3272 plot_lines.extend(lines)
3273
3274 plot_lines.append("1.0 ls 999 title ''")
3275
3276
3277 plot_lines.reverse()
3278
3279 gnuplot_out.append(',\\\n'.join(plot_lines))
3280
3281
3282 gnuplot_out.extend(['','unset label','',
3283 '################################################################################'])
3284
3285
3286 return block_position+len(self)
3287
3288
3289
3290
3291 -def plot_ratio_from_HWU(path, ax, hwu_variable, hwu_numerator, hwu_denominator, *args, **opts):
3292 """INPUT:
3293 - path can be a path to HwU or an HwUList instance
3294 - ax is the matplotlib frame where to do the plot
3295 - hwu_variable is the histograms to consider
3296 - hwu_numerator is the numerator of the ratio plot
3297 - hwu_denominator is the denominator of the ratio plot
3298 OUTPUT:
3299 - adding the curves to the plot
3300 - return the HwUList
3301 """
3302
3303 if isinstance(path, str):
3304 hwu = HwUList(path, raw_labels=True)
3305 else:
3306 hwu = path
3307
3308 if 'hwu_denominator_path' in opts:
3309 print('found second hwu')
3310 if isinstance(opts['hwu_denominator_path'],str):
3311 hwu2 = HwUList(path, raw_labels=True)
3312 else:
3313 hwu2 = opts['hwu_denominator_path']
3314 del opts['hwu_denominator_path']
3315 else:
3316 hwu2 = hwu
3317
3318
3319 select_hist = hwu.get(hwu_variable)
3320 select_hist2 = hwu2.get(hwu_variable)
3321 bins = select_hist.get('bins')
3322 num = select_hist.get(hwu_numerator)
3323 denom = select_hist2.get(hwu_denominator)
3324 ratio = [num[i]/denom[i] if denom[i] else 1 for i in range(len(bins))]
3325 if 'drawstyle' not in opts:
3326 opts['drawstyle'] = 'steps'
3327 ax.plot(bins, ratio, *args, **opts)
3328 return hwu
3329
3330 -def plot_from_HWU(path, ax, hwu_variable, hwu_central, *args, **opts):
3331 """INPUT:
3332 - path can be a path to HwU or an HwUList instance
3333 - ax is the matplotlib frame where to do the plot
3334 - hwu_variable is the histograms to consider
3335 - hwu_central is the central curve to consider
3336 - hwu_error is the error band to consider (optional: Default is no band)
3337 - hwu_error_mode is how to compute the error band (optional)
3338 OUTPUT:
3339 - adding the curves to the plot
3340 - return the HwUList
3341 - return the line associated to the central (can be used to get the color)
3342 """
3343
3344
3345 if 'hwu_error' in opts:
3346 hwu_error = opts['hwu_error']
3347 del opts['hwu_error']
3348 else:
3349 hwu_error = None
3350
3351 if 'hwu_error_mode' in opts:
3352 hwu_error_mode = opts['hwu_error_mode']
3353 del opts['hwu_error_mode']
3354 else:
3355 hwu_error_mode = None
3356
3357 if 'hwu_mult' in opts:
3358 hwu_mult = opts['hwu_mult']
3359 del opts['hwu_mult']
3360 else:
3361 hwu_mult = 1
3362
3363 if isinstance(path, str):
3364 hwu = HwUList(path, raw_labels=True)
3365 else:
3366 hwu = path
3367
3368
3369 select_hist = hwu.get(hwu_variable)
3370 bins = select_hist.get('bins')
3371 central_value = select_hist.get(hwu_central)
3372 if hwu_mult != 1:
3373 central_value = [hwu_mult*b for b in central_value]
3374 if 'drawstyle' not in opts:
3375 opts['drawstyle'] = 'steps'
3376 H, = ax.plot(bins, central_value, *args, **opts)
3377
3378
3379 if hwu_error:
3380 if not 'hwu_error_mode' in opts:
3381 opts['hwu_error_mode']=None
3382 h_min, h_max = select_hist.get_uncertainty_band(hwu_error, mode=hwu_error_mode)
3383 if hwu_mult != 1:
3384 h_min = [hwu_mult*b for b in h_min]
3385 h_max = [hwu_mult*b for b in h_max]
3386 fill_between_steps(bins, h_min, h_max, ax=ax, facecolor=H.get_color(),
3387 alpha=0.5, edgecolor=H.get_color(),hatch='/')
3388
3389 return hwu, H
3390
3391
3392
3393
3394
3395
3396 if __name__ == "__main__":
3397 main_doc = \
3398 """ For testing and standalone use. Usage:
3399 python histograms.py <.HwU input_file_path_1> <.HwU input_file_path_2> ... --out=<output_file_path.format> <options>
3400 Where <options> can be a list of the following:
3401 '--help' See this message.
3402 '--gnuplot' or '' output the histograms read to gnuplot
3403 '--HwU' to output the histograms read to the raw HwU source.
3404 '--types=<type1>,<type2>,...' to keep only the type<i> when importing histograms.
3405 '--titles=<title1>,<title2>,...' to keep only the titles which have any of 'title<i>' in them (not necessarily equal to them)
3406 '--n_ratios=<integer>' Specifies how many curves must be considerd for the ratios.
3407 '--no_open' Turn off the automatic processing of the gnuplot output.
3408 '--show_full' to show the complete output of what was read.
3409 '--show_short' to show a summary of what was read.
3410 '--simple_ratios' to turn off correlations and error propagation in the ratio.
3411 '--colours=<colour1>,<colour2>,...' to assign a non-default colour to GnuPlot histograms (max 8 colours)
3412 '--sum' To sum all identical histograms together
3413 '--average' To average over all identical histograms
3414 '--rebin=<n>' Rebin the plots by merging n-consecutive bins together.
3415 '--assign_types=<type1>,<type2>,...' to assign a type to all histograms of the first, second, etc... files loaded.
3416 '--multiply=<fact1>,<fact2>,...' to multiply all histograms of the first, second, etc... files by the fact1, fact2, etc...
3417 '--no_suffix' Do no add any suffix (like '#1, #2, etc..) to the histograms types.
3418 '--lhapdf-config=<PATH_TO_LHAPDF-CONFIG>' give path to lhapdf-config to compute PDF certainties using LHAPDF (only for lhapdf6)
3419 '--jet_samples=[int1,int2]' Specifies what jet samples to keep. 'None' is the default and keeps them all.
3420 '--central_only' This option specifies to disregard all extra weights, so as to make it possible
3421 to take the ratio of plots with different extra weights specified.
3422 '--keep_all_weights' This option specifies to keep in the HwU produced all the weights, even
3423 those which are not known (i.e. that is scale, PDF or merging variation)
3424 For chosing what kind of variation you want to see on your plot, you can use the following options
3425 '--no_<type>' Turn off the plotting of variations of the chosen type
3426 '--only_<type>' Turn on only the plotting of variations of the chosen type
3427 '--variations=['<type1>',...]' Turn on only the plotting of the variations of the list of chosen types
3428 '--band=['<type1>',...]' Chose for which variations one should use uncertainty bands as opposed to lines
3429 The types can be: pdf, scale, stat, merging or alpsfact
3430 For the last two options one can use ...=all to automatically select all types.
3431
3432 When parsing an XML-formatted plot source output by the Pythia8 driver, the file names can be appended
3433 options as suffixes separated by '|', as follows:
3434 python histograms.py <XML_source_file_name>@<option1>@<option2>@etc..
3435 These options can be
3436 'run_id=<integer>' Specifies the run_ID from which the plots should be loaded.
3437 By default, the first run is considered and the ones that follow are ignored.
3438 'merging_scale=<float>' This option allows to specify to import only the plots corresponding to a specific
3439 value for the merging scale.
3440 A value of -1 means that only the weights with the same merging scale as the central weight are kept.
3441 By default, all weights are considered.
3442 """
3443
3444 possible_options=['--help', '--gnuplot', '--HwU', '--types','--n_ratios',\
3445 '--no_open','--show_full','--show_short','--simple_ratios','--sum','--average','--rebin', \
3446 '--assign_types','--multiply','--no_suffix', '--out', '--jet_samples',
3447 '--no_scale','--no_pdf','--no_stat','--no_merging','--no_alpsfact',
3448 '--only_scale','--only_pdf','--only_stat','--only_merging','--only_alpsfact',
3449 '--variations','--band','--central_only', '--lhapdf-config','--titles',
3450 '--keep_all_weights','--colours']
3451 n_ratios = -1
3452 uncertainties = ['scale','pdf','statistical','merging_scale','alpsfact']
3453
3454 use_band = None
3455 auto_open = True
3456 ratio_correlations = True
3457 consider_reweights = ['pdf','scale','murmuf_scales','merging_scale','alpsfact']
3458
3459 - def log(msg):
3460 print("histograms.py :: %s"%str(msg))
3461
3462 if '--help' in sys.argv or len(sys.argv)==1:
3463 log('\n\n%s'%main_doc)
3464 sys.exit(0)
3465
3466 for arg in sys.argv[1:]:
3467 if arg.startswith('--'):
3468 if arg.split('=')[0] not in possible_options:
3469 log('WARNING: option "%s" not valid. It will be ignored' % arg)
3470
3471 arg_string=' '.join(sys.argv)
3472
3473 OutName = ""
3474 for arg in sys.argv[1:]:
3475 if arg.startswith('--out='):
3476 OutName = arg[6:]
3477
3478 accepted_types = []
3479 for arg in sys.argv[1:]:
3480 if arg.startswith('--types='):
3481 accepted_types = [(type if type!='None' else None) for type in \
3482 arg[8:].split(',')]
3483
3484 accepted_titles = []
3485 for arg in sys.argv[1:]:
3486 if arg.startswith('--titles='):
3487 accepted_titles = [(type if type!='None' else None) for type in \
3488 arg[9:].split(',')]
3489
3490 assigned_types = []
3491 for arg in sys.argv[1:]:
3492 if arg.startswith('--assign_types='):
3493 assigned_types = [(type if type!='None' else None) for type in \
3494 arg[15:].split(',')]
3495
3496 assigned_colours = []
3497 for arg in sys.argv[1:]:
3498 if arg.startswith('--colours='):
3499 assigned_colours = [(colour if colour!='None' else None) for colour in \
3500 arg[10:].split(',')]
3501
3502 jet_samples_to_keep = None
3503
3504 lhapdfconfig = ['lhapdf-config']
3505 for arg in sys.argv[1:]:
3506 if arg.startswith('--lhapdf-config='):
3507 lhapdfconfig = arg[16:]
3508
3509 no_suffix = False
3510 if '--no_suffix' in sys.argv:
3511 no_suffix = True
3512
3513 if '--central_only' in sys.argv:
3514 consider_reweights = []
3515
3516 if '--keep_all_weights' in sys.argv:
3517 consider_reweights = 'ALL'
3518
3519 for arg in sys.argv[1:]:
3520 if arg.startswith('--n_ratios='):
3521 n_ratios = int(arg[11:])
3522
3523 if '--no_open' in sys.argv:
3524 auto_open = False
3525
3526 variation_type_map={'scale':'scale','merging':'merging_scale','pdf':'pdf',
3527 'stat':'statistical','alpsfact':'alpsfact'}
3528
3529 for arg in sys.argv:
3530 try:
3531 opt, value = arg.split('=')
3532 except ValueError:
3533 continue
3534 if opt=='--jet_samples':
3535 jet_samples_to_keep = eval(value)
3536 if opt=='--variations':
3537 uncertainties=[variation_type_map[type] for type in eval(value,
3538 dict([(key,key) for key in variation_type_map.keys()]+
3539 [('all',list(variation_type_map.keys()))]))]
3540 if opt=='--band':
3541 use_band=[variation_type_map[type] for type in eval(value,
3542 dict([(key,key) for key in variation_type_map.keys()]+
3543 [('all',[type for type in variation_type_map.keys() if type!='stat'])]))]
3544
3545 if '--simple_ratios' in sys.argv:
3546 ratio_correlations = False
3547
3548 for arg in sys.argv:
3549 if arg.startswith('--no_') and not arg.startswith('--no_open'):
3550 uncertainties.remove(variation_type_map[arg[5:]])
3551 if arg.startswith('--only_'):
3552 uncertainties= [variation_type_map[arg[7:]]]
3553 break
3554
3555
3556
3557 if isinstance(consider_reweights, list):
3558 naming_map={'pdf':'pdf','scale':'scale',
3559 'merging_scale':'merging_scale','alpsfact':'alpsfact'}
3560 for key in naming_map:
3561 if (not key in uncertainties) and (naming_map[key] in consider_reweights):
3562 consider_reweights.remove(naming_map[key])
3563
3564 n_files = len([_ for _ in sys.argv[1:] if not _.startswith('--')])
3565 histo_norm = [1.0]*n_files
3566
3567 for arg in sys.argv[1:]:
3568 if arg.startswith('--multiply='):
3569 histo_norm = [(float(fact) if fact!='' else 1.0) for fact in \
3570 arg[11:].split(',')]
3571
3572 if '--average' in sys.argv:
3573 histo_norm = [hist/float(n_files) for hist in histo_norm]
3574
3575 log("=======")
3576 histo_list = HwUList([])
3577 for i, arg in enumerate(sys.argv[1:]):
3578 if arg.startswith('--'):
3579 break
3580 log("Loading histograms from '%s'."%arg)
3581 if OutName=="":
3582 OutName = os.path.basename(arg).split('.')[0]+'_output'
3583
3584 file_specification = arg.split('@')
3585 filename = file_specification.pop(0)
3586 file_options = {}
3587 for option in file_specification:
3588 opt, value = option.split('=')
3589 if opt=='run_id':
3590 file_options[opt]=int(value)
3591 if opt=='merging_scale':
3592 file_options[opt]=float(value)
3593 else:
3594 log("Unreckognize file option '%s'."%option)
3595 sys.exit(1)
3596 new_histo_list = HwUList(filename, accepted_types_order=accepted_types,
3597 consider_reweights=consider_reweights, **file_options)
3598
3599 if len(accepted_titles)>0:
3600 new_histo_list = HwUList(histo for histo in new_histo_list if
3601 any(t in histo.title for t in accepted_titles))
3602 for histo in new_histo_list:
3603 if no_suffix or n_files==1:
3604 continue
3605 if not histo.type is None:
3606 histo.type += '|'
3607 else:
3608 histo.type = ''
3609
3610
3611
3612
3613
3614
3615 try:
3616 suffix = assigned_types[i]
3617 except IndexError:
3618 suffix = "#%d"%(i+1)
3619 try:
3620 histo.type = histo.type[:histo.type.index('#')] + suffix
3621 except ValueError:
3622 histo.type += suffix
3623
3624 if i==0 or all(_ not in ['--sum','--average'] for _ in sys.argv):
3625 for j,hist in enumerate(new_histo_list):
3626 new_histo_list[j]=hist*histo_norm[i]
3627 histo_list.extend(new_histo_list)
3628 continue
3629
3630 if any(_ in sys.argv for _ in ['--sum','--average']):
3631 for j, hist in enumerate(new_histo_list):
3632
3633 hist.test_plot_compability(histo_list[j])
3634
3635 histo_list[j] += hist*histo_norm[i]
3636
3637 log("A total of %i histograms were found."%len(histo_list))
3638 log("=======")
3639
3640 n_rebin = 1
3641 for arg in sys.argv[1:]:
3642 if arg.startswith('--rebin='):
3643 n_rebin = int(arg[8:])
3644
3645 if n_rebin > 1:
3646 for hist in histo_list:
3647 hist.rebin(n_rebin)
3648
3649 if '--gnuplot' in sys.argv or all(arg not in ['--HwU'] for arg in sys.argv):
3650
3651 histo_list.output(OutName, format='gnuplot',
3652 number_of_ratios = n_ratios,
3653 uncertainties=uncertainties,
3654 ratio_correlations=ratio_correlations,
3655 arg_string=arg_string,
3656 jet_samples_to_keep=jet_samples_to_keep,
3657 use_band=use_band,
3658 auto_open=auto_open,
3659 lhapdfconfig=lhapdfconfig,
3660 assigned_colours=assigned_colours)
3661
3662 log("%d histograms have been output in " % len(histo_list)+\
3663 "the gnuplot format at '%s.[HwU|gnuplot]'." % OutName)
3664 if auto_open:
3665 command = 'gnuplot %s.gnuplot'%OutName
3666 try:
3667 subprocess.call(command,shell=True,stderr=subprocess.PIPE)
3668 except:
3669 log("Automatic processing of the gnuplot card failed. Try the"+\
3670 " command by hand:\n%s"%command)
3671 else:
3672 sys.exit(0)
3673
3674 if '--HwU' in sys.argv:
3675 log("Histograms data has been output in the HwU format at "+\
3676 "'%s.HwU'."%OutName)
3677 histo_list.output(OutName, format='HwU')
3678 sys.exit(0)
3679
3680 if '--show_short' in sys.argv or '--show_full' in sys.argv:
3681 for i, histo in enumerate(histo_list):
3682 if i!=0:
3683 log('-------')
3684 log(histo.nice_string(short=(not '--show_full' in sys.argv)))
3685 log("=======")
3690 ''' Fills a hole in matplotlib: fill_between for step plots.
3691 Parameters :
3692 ------------
3693 x : array-like
3694 Array/vector of index values. These are assumed to be equally-spaced.
3695 If not, the result will probably look weird...
3696 y1 : array-like
3697 Array/vector of values to be filled under.
3698 y2 : array-Like
3699 Array/vector or bottom values for filled area. Default is 0.
3700 **kwargs will be passed to the matplotlib fill_between() function.
3701 '''
3702
3703 if ax is None:
3704 ax = plt.gca()
3705
3706
3707
3708
3709 xx= []; [(xx.append(d),xx.append(d)) for d in x]; xx = xx[1:]
3710
3711 xstep = x[1] -x[0]
3712
3713 xx.append(xx[-1] + xstep)
3714
3715
3716 if h_align == 'mid':
3717 xx = [X-xstep/2. for X in xx]
3718 elif h_align == 'right':
3719 xx = [X-xstep for X in xx]
3720
3721
3722 yy1 = []; [(yy1.append(d),yy1.append(d)) for d in y1]
3723 if isinstance(y1, list):
3724 yy2 = []; [(yy2.append(d),yy2.append(d)) for d in y2]
3725 else:
3726 yy2=y2
3727 if len(yy2) != len(yy1):
3728 yy2 = []; [(yy2.append(d),yy2.append(d)) for d in y2]
3729
3730
3731 ax.fill_between(xx, yy1, y2=yy2, **kwargs)
3732
3733 return ax
3734
3735