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