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, consider_reweights='ALL', **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 consider_reweights=consider_reweights, 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
1072 """ Reads *one* histogram from a stream, with the mandatory specification
1073 of the ordered list of weight names. Return True or False depending
1074 on whether the starting definition of a new plot could be found in this
1075 stream."""
1076 n_bins = 0
1077
1078 if consider_reweights=='ALL' or raw_labels:
1079 weight_header = all_weight_header
1080 else:
1081 new_weight_header = []
1082
1083 for wgt_label in all_weight_header:
1084 if wgt_label in ['central','stat_error','boundary_xmin','boundary_xmax'] or\
1085 HwU.get_HwU_wgt_label_type(wgt_label) in consider_reweights:
1086 new_weight_header.append(wgt_label)
1087 weight_header = new_weight_header
1088
1089
1090 for line in stream:
1091 start = HwU.histo_start_re.match(line)
1092 if not start is None:
1093 self.process_histogram_name(start.group('histo_name'))
1094
1095
1096 if self.type == 'AUX':
1097 continue
1098 n_bins = int(start.group('n_bins'))
1099
1100
1101 self.bins = BinList(weight_labels = [ wgt_label for
1102 wgt_label in weight_header if wgt_label not in
1103 ['boundary_xmin','boundary_xmax']])
1104 break
1105
1106
1107 for line_bin in stream:
1108 bin_weights = {}
1109 boundaries = [0.0,0.0]
1110 for j, weight in \
1111 enumerate(HwU.histo_bin_weight_re.finditer(line_bin)):
1112 if j == len(all_weight_header):
1113 raise HwU.ParseError, "There is more bin weights"+\
1114 " specified than expected (%i)"%len(weight_header)
1115 if all_weight_header[j] == 'boundary_xmin':
1116 boundaries[0] = float(weight.group('weight'))
1117 elif all_weight_header[j] == 'boundary_xmax':
1118 boundaries[1] = float(weight.group('weight'))
1119 elif all_weight_header[j] in weight_header:
1120 bin_weights[all_weight_header[j]] = \
1121 float(weight.group('weight'))
1122
1123
1124
1125
1126 if len(bin_weights)<(len(weight_header)-2):
1127 raise HwU.ParseError, " There are only %i weights"\
1128 %len(bin_weights)+" specified and %i were expected."%\
1129 (len(weight_header)-2)
1130 self.bins.append(Bin(tuple(boundaries), bin_weights))
1131 if len(self.bins)==n_bins:
1132 break
1133
1134 if len(self.bins)!=n_bins:
1135 raise HwU.ParseError, "%i bin specification "%len(self.bins)+\
1136 "were found and %i were expected."%n_bins
1137
1138
1139 for line_end in stream:
1140 if HwU.histo_end_re.match(line_end):
1141
1142
1143 if not raw_labels:
1144 self.trim_auxiliary_weights()
1145
1146 return True
1147
1148
1149 return False
1150
1152 """ Remove all weights which are auxiliary (whose name end with '@aux')
1153 so that they are not included (they will be regenerated anyway)."""
1154
1155 for i, wgt_label in enumerate(self.bins.weight_labels):
1156 if isinstance(wgt_label, str) and wgt_label.endswith('@aux'):
1157 for bin in self.bins:
1158 try:
1159 del bin.wgts[wgt_label]
1160 except KeyError:
1161 pass
1162 self.bins.weight_labels = [wgt_label for wgt_label in
1163 self.bins.weight_labels if (not isinstance(wgt_label, str)
1164 or (isinstance(wgt_label, str) and not wgt_label.endswith('@aux')) )]
1165
1166 - def set_uncertainty(self, type='all_scale',lhapdfconfig='lhapdf-config'):
1167 """ Adds a weight to the bins which is the envelope of the scale
1168 uncertainty, for the scale specified which can be either 'mur', 'muf',
1169 'all_scale' or 'PDF'."""
1170
1171 if type.upper()=='MUR':
1172 new_wgt_label = 'delta_mur'
1173 scale_position = 1
1174 elif type.upper()=='MUF':
1175 new_wgt_label = 'delta_muf'
1176 scale_position = 2
1177 elif type.upper()=='ALL_SCALE':
1178 new_wgt_label = 'delta_mu'
1179 scale_position = -1
1180 elif type.upper()=='PDF':
1181 new_wgt_label = 'delta_pdf'
1182 scale_position = -2
1183 elif type.upper()=='MERGING':
1184 new_wgt_label = 'delta_merging'
1185 elif type.upper()=='ALPSFACT':
1186 new_wgt_label = 'delta_alpsfact'
1187 else:
1188 raise MadGraph5Error, ' The function set_uncertainty can'+\
1189 " only handle the scales 'mur', 'muf', 'all_scale', 'pdf',"+\
1190 "'merging' or 'alpsfact'."
1191
1192 wgts_to_consider=[]
1193 label_to_consider=[]
1194 if type.upper() == 'MERGING':
1195
1196
1197
1198 wgts_to_consider.append([ label for label in self.bins.weight_labels if \
1199 HwU.get_HwU_wgt_label_type(label)=='merging_scale' ])
1200 label_to_consider.append('none')
1201
1202 elif type.upper() == 'ALPSFACT':
1203
1204
1205
1206 wgts_to_consider.append([ label for label in self.bins.weight_labels if \
1207 HwU.get_HwU_wgt_label_type(label)=='alpsfact' ])
1208 label_to_consider.append('none')
1209 elif scale_position > -2:
1210
1211 dyn_scales=[label[1] for label in self.bins.weight_labels if \
1212 HwU.get_HwU_wgt_label_type(label)=='scale_adv']
1213
1214 dyn_scales=[scale for n,scale in enumerate(dyn_scales) if scale not in dyn_scales[:n]]
1215 for dyn_scale in dyn_scales:
1216 wgts=[label for label in self.bins.weight_labels if \
1217 HwU.get_HwU_wgt_label_type(label)=='scale_adv' and label[1]==dyn_scale]
1218 if wgts:
1219 wgts_to_consider.append(wgts)
1220 label_to_consider.append(dyn_scale)
1221
1222 wgts=[label for label in self.bins.weight_labels if \
1223 HwU.get_HwU_wgt_label_type(label)=='scale']
1224
1225
1226
1227
1228 if wgts:
1229 wgts_to_consider.append(wgts)
1230 label_to_consider.append('none')
1231
1232
1233 if scale_position > -1:
1234 for wgts in wgts_to_consider:
1235 wgts_to_consider.remove(wgts)
1236 wgts = [ label for label in wgts if label[-scale_position]==1.0 ]
1237 wgts_to_consider.append(wgts)
1238 elif scale_position == -2:
1239
1240 pdf_sets=[label[2] for label in self.bins.weight_labels if \
1241 HwU.get_HwU_wgt_label_type(label)=='pdf_adv']
1242
1243 pdf_sets=[ii for n,ii in enumerate(pdf_sets) if ii not in pdf_sets[:n]]
1244 for pdf_set in pdf_sets:
1245 wgts=[label for label in self.bins.weight_labels if \
1246 HwU.get_HwU_wgt_label_type(label)=='pdf_adv' and label[2]==pdf_set]
1247 if wgts:
1248 wgts_to_consider.append(wgts)
1249 label_to_consider.append(pdf_set)
1250
1251 wgts = [ label for label in self.bins.weight_labels if \
1252 HwU.get_HwU_wgt_label_type(label)=='pdf']
1253 if wgts:
1254 wgts_to_consider.append(wgts)
1255 label_to_consider.append('none')
1256
1257 if len(wgts_to_consider)==0 or all(len(wgts)==0 for wgts in wgts_to_consider):
1258
1259 return (None,[None])
1260
1261
1262 if type=='PDF':
1263 use_lhapdf=False
1264 try:
1265 lhapdf_libdir=subprocess.Popen([lhapdfconfig,'--libdir'],\
1266 stdout=subprocess.PIPE).stdout.read().strip()
1267 except:
1268 use_lhapdf=False
1269 else:
1270 try:
1271 candidates=[dirname for dirname in os.listdir(lhapdf_libdir) \
1272 if os.path.isdir(os.path.join(lhapdf_libdir,dirname))]
1273 except OSError:
1274 candidates=[]
1275 for candidate in candidates:
1276 if os.path.isfile(os.path.join(lhapdf_libdir,candidate,'site-packages','lhapdf.so')):
1277 sys.path.insert(0,os.path.join(lhapdf_libdir,candidate,'site-packages'))
1278 try:
1279 import lhapdf
1280 use_lhapdf=True
1281 break
1282 except ImportError:
1283 sys.path.pop(0)
1284 continue
1285
1286 if not use_lhapdf:
1287 try:
1288 candidates=[dirname for dirname in os.listdir(lhapdf_libdir+'64') \
1289 if os.path.isdir(os.path.join(lhapdf_libdir+'64',dirname))]
1290 except OSError:
1291 candidates=[]
1292 for candidate in candidates:
1293 if os.path.isfile(os.path.join(lhapdf_libdir+'64',candidate,'site-packages','lhapdf.so')):
1294 sys.path.insert(0,os.path.join(lhapdf_libdir+'64',candidate,'site-packages'))
1295 try:
1296 import lhapdf
1297 use_lhapdf=True
1298 break
1299 except ImportError:
1300 sys.path.pop(0)
1301 continue
1302
1303 if not use_lhapdf:
1304 try:
1305 import lhapdf
1306 use_lhapdf=True
1307 except ImportError:
1308 logger.warning("Failed to access python version of LHAPDF: "\
1309 "cannot compute PDF uncertainty from the "\
1310 "weights in the histograms. The weights in the HwU data files " \
1311 "still cover all PDF set members, "\
1312 "but the automatic computation of the uncertainties from "\
1313 "those weights might not be correct. \n "\
1314 "If the python interface to LHAPDF is available on your system, try "\
1315 "adding its location to the PYTHONPATH environment variable and the"\
1316 "LHAPDF library location to LD_LIBRARY_PATH (linux) or DYLD_LIBRARY_PATH (mac os x).")
1317
1318 if type=='PDF' and use_lhapdf:
1319 lhapdf.setVerbosity(0)
1320
1321
1322 position=[]
1323 labels=[]
1324 for i,label in enumerate(label_to_consider):
1325 wgts=wgts_to_consider[i]
1326 if label != 'none':
1327 new_wgt_labels=['%s_cen %s @aux' % (new_wgt_label,label),
1328 '%s_min %s @aux' % (new_wgt_label,label),
1329 '%s_max %s @aux' % (new_wgt_label,label)]
1330 else:
1331 new_wgt_labels=['%s_cen @aux' % new_wgt_label,
1332 '%s_min @aux' % new_wgt_label,
1333 '%s_max @aux' % new_wgt_label]
1334 try:
1335 pos=[(not isinstance(lab, str)) for lab in \
1336 self.bins.weight_labels].index(True)
1337 position.append(pos)
1338 labels.append(label)
1339 self.bins.weight_labels = self.bins.weight_labels[:pos]+\
1340 new_wgt_labels + self.bins.weight_labels[pos:]
1341 except ValueError:
1342 pos=len(self.bins.weight_labels)
1343 position.append(pos)
1344 labels.append(label)
1345 self.bins.weight_labels.extend(new_wgt_labels)
1346
1347 if type=='PDF' and use_lhapdf and label != 'none':
1348 p=lhapdf.getPDFSet(label)
1349
1350
1351 for bin in self.bins:
1352 if type!='PDF':
1353 bin.wgts[new_wgt_labels[0]] = bin.wgts[wgts[0]]
1354 bin.wgts[new_wgt_labels[1]] = min(bin.wgts[label] \
1355 for label in wgts)
1356 bin.wgts[new_wgt_labels[2]] = max(bin.wgts[label] \
1357 for label in wgts)
1358 elif type=='PDF' and use_lhapdf and label != 'none' and len(wgts) > 1:
1359 pdfs = [bin.wgts[pdf] for pdf in sorted(wgts)]
1360 ep=p.uncertainty(pdfs,-1)
1361 bin.wgts[new_wgt_labels[0]] = ep.central
1362 bin.wgts[new_wgt_labels[1]] = ep.central-ep.errminus
1363 bin.wgts[new_wgt_labels[2]] = ep.central+ep.errplus
1364 elif type=='PDF' and use_lhapdf and label != 'none' and len(bin.wgts) == 1:
1365 bin.wgts[new_wgt_labels[0]] = bin.wgts[wgts[0]]
1366 bin.wgts[new_wgt_labels[1]] = bin.wgts[wgts[0]]
1367 bin.wgts[new_wgt_labels[2]] = bin.wgts[wgts[0]]
1368 else:
1369 pdfs = [bin.wgts[pdf] for pdf in sorted(wgts)]
1370 pdf_up = 0.0
1371 pdf_down = 0.0
1372 cntrl_val = bin.wgts['central']
1373 if wgts[0] <= 90000:
1374
1375 if len(pdfs)>2:
1376 for i in range(int((len(pdfs)-1)/2)):
1377 pdf_up += max(0.0,pdfs[2*i+1]-cntrl_val,
1378 pdfs[2*i+2]-cntrl_val)**2
1379 pdf_down += max(0.0,cntrl_val-pdfs[2*i+1],
1380 cntrl_val-pdfs[2*i+2])**2
1381 pdf_up = cntrl_val + math.sqrt(pdf_up)
1382 pdf_down = cntrl_val - math.sqrt(pdf_down)
1383 else:
1384 pdf_up = bin.wgts[pdfs[0]]
1385 pdf_down = bin.wgts[pdfs[0]]
1386 elif wgts[0] in range(90200, 90303) or \
1387 wgts[0] in range(90400, 90433) or \
1388 wgts[0] in range(90700, 90801) or \
1389 wgts[0] in range(90900, 90931) or \
1390 wgts[0] in range(91200, 91303) or \
1391 wgts[0] in range(91400, 91433) or \
1392 wgts[0] in range(91700, 91801) or \
1393 wgts[0] in range(91900, 90931):
1394
1395 pdf_stdev = 0.0
1396 for pdf in pdfs[1:]:
1397 pdf_stdev += (pdf - cntrl_val)**2
1398 pdf_stdev = math.sqrt(pdf_stdev)
1399 pdf_up = cntrl_val+pdf_stdev
1400 pdf_down = cntrl_val-pdf_stdev
1401 else:
1402
1403 pdf_stdev = 0.0
1404 for pdf in pdfs[1:]:
1405 pdf_stdev += (pdf - cntrl_val)**2
1406 pdf_stdev = math.sqrt(pdf_stdev/float(len(pdfs)-2))
1407 pdf_up = cntrl_val+pdf_stdev
1408 pdf_down = cntrl_val-pdf_stdev
1409
1410 bin.wgts[new_wgt_labels[0]] = bin.wgts[wgts[0]]
1411 bin.wgts[new_wgt_labels[1]] = pdf_down
1412 bin.wgts[new_wgt_labels[2]] = pdf_up
1413
1414
1415
1416 return (position,labels)
1417
1418 - def rebin(self, n_rebin):
1419 """ Rebin the x-axis so as to merge n_rebin consecutive bins into a
1420 single one. """
1421
1422 if n_rebin < 1 or not isinstance(n_rebin, int):
1423 raise MadGraph5Error, "The argument 'n_rebin' of the HwU function"+\
1424 " 'rebin' must be larger or equal to 1, not '%s'."%str(n_rebin)
1425 elif n_rebin==1:
1426 return
1427
1428 if self.type and 'NOREBIN' in self.type.upper():
1429 return
1430
1431 rebinning_list = list(range(0,len(self.bins),n_rebin))+[len(self.bins),]
1432 concat_list = [self.bins[rebinning_list[i]:rebinning_list[i+1]] for \
1433 i in range(len(rebinning_list)-1)]
1434
1435 new_bins = copy.copy(self.bins)
1436 del new_bins[:]
1437
1438 for bins_to_merge in concat_list:
1439 if len(bins_to_merge)==0:
1440 continue
1441 new_bins.append(Bin(boundaries=(bins_to_merge[0].boundaries[0],
1442 bins_to_merge[-1].boundaries[1]),wgts={'central':0.0}))
1443 for weight in self.bins.weight_labels:
1444 if weight != 'stat_error':
1445 new_bins[-1].wgts[weight] = \
1446 sum(b.wgts[weight] for b in bins_to_merge)
1447 else:
1448 new_bins[-1].wgts['stat_error'] = \
1449 math.sqrt(sum(b.wgts['stat_error']**2 for b in\
1450 bins_to_merge))
1451
1452 self.bins = new_bins
1453
1454 @classmethod
1456 """ Function to determine the optimal x-axis range when plotting
1457 together the histos in histo_list and considering the weights
1458 weight_labels"""
1459
1460
1461 if weight_labels is None:
1462 weight_labels = histo_list[0].bins.weight_labels
1463
1464 all_boundaries = sum([ list(bin.boundaries) for histo in histo_list \
1465 for bin in histo.bins if \
1466 (sum(abs(bin.wgts[label]) for label in weight_labels) > 0.0)] ,[])
1467
1468 if len(all_boundaries)==0:
1469 all_boundaries = sum([ list(bin.boundaries) for histo in histo_list \
1470 for bin in histo.bins],[])
1471 if len(all_boundaries)==0:
1472 raise MadGraph5Error, "The histograms with title '%s'"\
1473 %histo_list[0].title+" seems to have no bins."
1474
1475 x_min = min(all_boundaries)
1476 x_max = max(all_boundaries)
1477
1478 return (x_min, x_max)
1479
1480 @classmethod
1483 """ Function to determine the optimal y-axis range when plotting
1484 together the histos in histo_list and considering the weights
1485 weight_labels. The option Kratio is present to allow for the couple of
1486 tweaks necessary for the the K-factor ratio histogram y-range."""
1487
1488
1489 if labels is None:
1490 weight_labels = histo_list[0].bins.weight_labels
1491 else:
1492 weight_labels = labels
1493
1494 all_weights = []
1495 for histo in histo_list:
1496 for bin in histo.bins:
1497 for label in weight_labels:
1498
1499
1500 if Kratio and bin.wgts[label]==0.0:
1501 continue
1502 if scale!='LOG':
1503 all_weights.append(bin.wgts[label])
1504 if label == 'stat_error':
1505 all_weights.append(-bin.wgts[label])
1506 elif bin.wgts[label]>0.0:
1507 all_weights.append(bin.wgts[label])
1508
1509
1510 sum([ [bin.wgts[label] for label in weight_labels if \
1511 (scale!='LOG' or bin.wgts[label]!=0.0)] \
1512 for histo in histo_list for bin in histo.bins], [])
1513
1514 all_weights.sort()
1515 if len(all_weights)!=0:
1516 partial_max = all_weights[int(len(all_weights)*0.95)]
1517 partial_min = all_weights[int(len(all_weights)*0.05)]
1518 max = all_weights[-1]
1519 min = all_weights[0]
1520 else:
1521 if scale!='LOG':
1522 return (0.0,1.0)
1523 else:
1524 return (1.0,10.0)
1525
1526 y_max = 0.0
1527 y_min = 0.0
1528
1529
1530 if (max-partial_max)>2.0*(partial_max-partial_min):
1531 y_max = partial_max
1532 else:
1533 y_max = max
1534
1535
1536 if (partial_min - min)>2.0*(partial_max-partial_min) and min != 0.0:
1537 y_min = partial_min
1538 else:
1539 y_min = min
1540
1541 if Kratio:
1542 median = all_weights[len(all_weights)//2]
1543 spread = (y_max-y_min)
1544 if abs(y_max-median)<spread*0.05 or abs(median-y_min)<spread*0.05:
1545 y_max = median + spread/2.0
1546 y_min = median - spread/2.0
1547 if y_min != y_max:
1548 return ( y_min , y_max )
1549
1550
1551 if len(histo_list[0].bins) <= 5:
1552 y_min = min
1553 y_max = max
1554
1555
1556 if y_min == y_max:
1557 if max == min:
1558 y_min -= 1.0
1559 y_max += 1.0
1560 else:
1561 y_min = min
1562 y_max = max
1563
1564 return ( y_min , y_max )
1565
1566 -class HwUList(histograms_PhysicsObjectList):
1567 """ A class implementing features related to a list of Hwu Histograms. """
1568
1569
1570
1571
1572 number_line_colors_defined = 8
1573
1575 """Test wether specified object is of the right type for this list."""
1576
1577 return isinstance(obj, HwU) or isinstance(obj, HwUList)
1578
1579 - def __init__(self, file_path, weight_header=None, run_id=None,
1580 merging_scale=None, accepted_types_order=[], consider_reweights='ALL',
1581 raw_labels=False, **opts):
1582 """ Read one plot from a file_path or a stream.
1583 This constructor reads all plots specified in target file.
1584 File_path can be a path or a stream in the argument.
1585 The option weight_header specifies an ordered list of weight names
1586 to appear in the file or stream specified. It accepted_types_order is
1587 empty, no filter is applied, otherwise only histograms of the specified
1588 types will be kept, and in this specified order for a given identical
1589 title. The option 'consider_reweights' selects whether one wants to
1590 include all the extra scale/pdf/merging variation weights. Possible values
1591 are 'ALL' or a list of the return types of the function get_HwU_wgt_label_type().
1592 The option 'raw_labels' specifies that one wants to import the
1593 histogram data with no treatment of the weight labels at all
1594 (this is used for the matplotlib output).
1595 """
1596
1597 if isinstance(file_path, str):
1598 stream = open(file_path,'r')
1599 elif isinstance(file_path, file):
1600 stream = file_path
1601 else:
1602 return super(HwUList,self).__init__(file_path, **opts)
1603
1604 try:
1605
1606 self.parse_histos_from_PY8_XML_stream(stream, run_id,
1607 merging_scale, accepted_types_order,
1608 consider_reweights=consider_reweights,
1609 raw_labels=raw_labels)
1610 except XMLParsingError:
1611
1612 stream.seek(0)
1613
1614 if not weight_header:
1615 weight_header = HwU.parse_weight_header(stream,raw_labels=raw_labels)
1616
1617 new_histo = HwU(stream, weight_header,raw_labels=raw_labels,
1618 consider_reweights=consider_reweights)
1619 while not new_histo.bins is None:
1620 if accepted_types_order==[] or \
1621 new_histo.type in accepted_types_order:
1622 self.append(new_histo)
1623 new_histo = HwU(stream, weight_header, raw_labels=raw_labels,
1624 consider_reweights=consider_reweights)
1625
1626 if not run_id is None:
1627 logger.info("The run_id '%s' was specified, but "%run_id+
1628 "format of the HwU plot source is the MG5aMC"+
1629 " so that the run_id information is ignored.")
1630
1631
1632 titles_order = [h.title for h in self]
1633 def ordering_function(histo):
1634 title_position = titles_order.index(histo.title)
1635 if accepted_types_order==[]:
1636 type_precedence = {'NLO':1,'LO':2,None:3,'AUX':5}
1637 try:
1638 ordering_key = (title_position,type_precedence[histo.type])
1639 except KeyError:
1640 ordering_key = (title_position,4)
1641 else:
1642 ordering_key = (title_position,
1643 accepted_types_order.index(histo.type))
1644 return ordering_key
1645
1646
1647
1648
1649
1650 self.sort(key=ordering_function)
1651
1652
1653 if isinstance(file_path, str):
1654 stream.close()
1655
1663
1665 """ return the list of all weights define in each histograms"""
1666
1667 return self[0].bins.weight_labels
1668
1669
1670 - def get(self, name):
1671 """return the HWU histograms related to a given name"""
1672 for hist in self:
1673 if hist.get_HwU_histogram_name() == name:
1674 return hist
1675
1676 raise NameError, "no histogram with name: %s" % name
1677
1678 - def parse_histos_from_PY8_XML_stream(self, stream, run_id=None,
1679 merging_scale=None, accepted_types_order=[],
1680 consider_reweights='ALL', raw_labels=False):
1681 """Initialize the HwU histograms from an XML stream. Only one run is
1682 used: the first one if run_id is None or the specified run otherwise.
1683 Accepted type order is a filter to select histograms of only a certain
1684 type. The option 'consider_reweights' selects whether one wants to
1685 include all the extra scale/pdf/merging variation weights.
1686 Possible values are 'ALL' or a list of the return types of the
1687 function get_HwU_wgt_label_type()."""
1688
1689 run_nodes = minidom.parse(stream).getElementsByTagName("run")
1690 all_nodes = dict((int(node.getAttribute('id')),node) for
1691 node in run_nodes)
1692 selected_run_node = None
1693 weight_header = None
1694 if run_id is None:
1695 if len(run_nodes)>0:
1696 selected_run_node = all_nodes[min(all_nodes.keys())]
1697 else:
1698 try:
1699 selected_run_node = all_nodes[int(run_id)]
1700 except:
1701 selected_run_node = None
1702
1703 if selected_run_node is None:
1704 if run_id is None:
1705 raise MadGraph5Error, \
1706 'No histogram was found in the specified XML source.'
1707 else:
1708 raise MadGraph5Error, \
1709 "Histogram with run_id '%d' was not found in the "%run_id+\
1710 "specified XML source."
1711
1712
1713
1714 if raw_labels:
1715
1716 weight_label_list = [wgt.strip() for wgt in
1717 str(selected_run_node.getAttribute('header')).split(';') if
1718 not re.match('^\s*$',wgt)]
1719 ordered_weight_label_list = [w for w in weight_label_list if w not\
1720 in ['xmin','xmax']]
1721
1722 filtered_ordered_weight_label_list = []
1723 for wgt_label in ordered_weight_label_list:
1724 if wgt_label not in filtered_ordered_weight_label_list:
1725 filtered_ordered_weight_label_list.append(wgt_label)
1726
1727 selected_weights = dict([ (wgt_pos,
1728 [wgt if wgt not in ['xmin','xmax'] else HwU.mandatory_weights[wgt]])
1729 for wgt_pos, wgt in enumerate(weight_label_list) if wgt in
1730 filtered_ordered_weight_label_list+['xmin','xmax']])
1731
1732 return self.retrieve_plots_from_XML_source(selected_run_node,
1733 selected_weights, filtered_ordered_weight_label_list,
1734 raw_labels=True)
1735
1736
1737
1738
1739 all_weights = []
1740 for wgt_position, wgt_label in \
1741 enumerate(str(selected_run_node.getAttribute('header')).split(';')):
1742 if not re.match('^\s*$',wgt_label) is None:
1743 continue
1744 all_weights.append({'POSITION':wgt_position})
1745 for wgt_item in wgt_label.strip().split('_'):
1746 property = wgt_item.strip().split('=')
1747 if len(property) == 2:
1748 all_weights[-1][property[0].strip()] = property[1].strip()
1749 elif len(property)==1:
1750 all_weights[-1][property[0].strip()] = None
1751 else:
1752 raise MadGraph5Error, \
1753 "The weight label property %s could not be parsed."%wgt_item
1754
1755
1756
1757
1758
1759 for wgt_label in all_weights:
1760 for mandatory_attribute in ['PDF','MUR','MUF','MERGING','ALPSFACT']:
1761 if mandatory_attribute not in wgt_label:
1762 wgt_label[mandatory_attribute] = '-1'
1763 if mandatory_attribute=='PDF':
1764 wgt_label[mandatory_attribute] = int(wgt_label[mandatory_attribute])
1765 elif mandatory_attribute in ['MUR','MUF','MERGING','ALPSFACT']:
1766 wgt_label[mandatory_attribute] = float(wgt_label[mandatory_attribute])
1767
1768
1769
1770
1771 if merging_scale is None or merging_scale < 0.0:
1772 merging_scale_chosen = all_weights[2]['MERGING']
1773 else:
1774 merging_scale_chosen = merging_scale
1775
1776
1777 central_PDF = all_weights[2]['PDF']
1778
1779 central_MUR = all_weights[2]['MUR'] if all_weights[2]['MUR']!=-1.0 else 1.0
1780 central_MUF = all_weights[2]['MUF'] if all_weights[2]['MUF']!=-1.0 else 1.0
1781 central_alpsfact = all_weights[2]['ALPSFACT'] if all_weights[2]['ALPSFACT']!=-1.0 else 1.0
1782
1783
1784
1785 selected_weights = {}
1786
1787 if 'xmin' not in all_weights[0] or \
1788 'xmax' not in all_weights[1] or \
1789 'Weight' not in all_weights[2] or \
1790 'WeightError' not in all_weights[3]:
1791 raise MadGraph5Error, 'The first weight entries in the XML HwU '+\
1792 ' source are not the standard expected ones (xmin, xmax, sigmaCentral, errorCentral)'
1793 selected_weights[0] = ['xmin']
1794 selected_weights[1] = ['xmax']
1795
1796
1797 def get_difference_to_central(weight):
1798 """ Return the list of properties which differ from the central weight.
1799 This disregards the merging scale value for which any central value
1800 can be picked anyway."""
1801
1802 differences = []
1803
1804
1805
1806 if 'Weight' in weight:
1807 return set([])
1808 if weight['MUR'] not in [central_MUR, -1.0] or \
1809 weight['MUF'] not in [central_MUF, -1.0]:
1810 differences.append('mur_muf_scale')
1811 if weight['PDF'] not in [central_PDF,-1]:
1812 differences.append('pdf')
1813 if weight['ALPSFACT'] not in [central_alpsfact, -1]:
1814 differences.append('ALPSFACT')
1815 return set(differences)
1816
1817 def format_weight_label(weight):
1818 """ Print the weight attributes in a nice order."""
1819
1820 all_properties = weight.keys()
1821 all_properties.pop(all_properties.index('POSITION'))
1822 ordered_properties = []
1823
1824 for property in all_properties:
1825 if weight[property] is None:
1826 ordered_properties.append(property)
1827
1828 ordered_properties.sort()
1829 all_properties = [property for property in all_properties if
1830 not weight[property] is None]
1831
1832
1833 for property in ['PDF','MUR','MUF','ALPSFACT','MERGING']:
1834 all_properties.pop(all_properties.index(property))
1835 if weight[property]!=-1:
1836 ordered_properties.append(property)
1837
1838 ordered_properties.extend(sorted(all_properties))
1839
1840 return '_'.join('%s%s'\
1841 %(key,'' if weight[key] is None else '=%s'%str(weight[key])) for
1842 key in ordered_properties)
1843
1844
1845
1846
1847
1848 if float(all_weights[2]['MERGING']) == merging_scale_chosen:
1849 selected_weights[2]=['central value']
1850 else:
1851 for weight_position, weight in enumerate(all_weights):
1852
1853
1854 if get_difference_to_central(weight)==set([]):
1855
1856 if weight['MERGING']==merging_scale_chosen:
1857 selected_weights[weight_position] = ['central value']
1858 break
1859
1860 if 'central value' not in sum(selected_weights.values(),[]):
1861 central_merging_scale = all_weights[2]['MERGING']
1862 logger.warning('Could not find the central weight for the'+\
1863 ' chosen merging scale (%f).\n'%merging_scale_chosen+\
1864 'MG5aMC will chose the original central scale provided which '+\
1865 'correspond to a merging scale of %s'%("'inclusive'" if
1866 central_merging_scale in [0.0,-1.0] else '%f'%central_merging_scale))
1867 selected_weights[2]=['central value']
1868
1869
1870 selected_weights[3]=['dy']
1871
1872
1873 for weight_position, weight in enumerate(all_weights[4:]):
1874
1875
1876
1877
1878
1879
1880 variations = get_difference_to_central(weight)
1881
1882
1883
1884
1885
1886
1887
1888 if variations in [set(['mur_muf_scale']),set(['pdf','mur_muf_scale'])]:
1889 wgt_label = ('scale',weight['MUR'],weight['MUF'])
1890 if variations in [set(['ALPSFACT']),set(['pdf','ALPSFACT'])]:
1891 wgt_label = ('alpsfact',weight['ALPSFACT'])
1892 if variations == set(['pdf']):
1893 wgt_label = ('pdf',weight['PDF'])
1894 if variations == set([]):
1895
1896 wgt_label = format_weight_label(weight)
1897
1898
1899 if weight['MERGING'] != merging_scale_chosen:
1900
1901 if merging_scale:
1902 continue
1903
1904
1905 if variations == set([]):
1906
1907 wgt_label = ('merging_scale', weight['MERGING'])
1908
1909
1910
1911 if wgt_label in sum(selected_weights.values(),[]):
1912 continue
1913
1914
1915 try:
1916 selected_weights[weight_position+4].append(wgt_label)
1917 except KeyError:
1918 selected_weights[weight_position+4]=[wgt_label,]
1919
1920 if merging_scale and merging_scale > 0.0 and \
1921 len(sum(selected_weights.values(),[]))==4:
1922 logger.warning('No additional variation weight was found for the '+\
1923 'chosen merging scale %f.'%merging_scale)
1924
1925
1926 for wgt_pos in selected_weights:
1927 for i, weight_label in enumerate(selected_weights[wgt_pos]):
1928 try:
1929 selected_weights[wgt_pos][i] = HwU.mandatory_weights[weight_label]
1930 except KeyError:
1931 pass
1932
1933
1934 if consider_reweights!='ALL':
1935 new_selected_weights = {}
1936 for wgt_position, wgt_labels in selected_weights.items():
1937 for wgt_label in wgt_labels:
1938 if wgt_label in ['central','stat_error','boundary_xmin','boundary_xmax'] or\
1939 HwU.get_HwU_wgt_label_type(wgt_label) in consider_reweights:
1940 try:
1941 new_selected_weights[wgt_position].append(wgt_label)
1942 except KeyError:
1943 new_selected_weights[wgt_position] = [wgt_label]
1944 selected_weights = new_selected_weights
1945
1946
1947 weight_label_list = sum(selected_weights.values(),[])
1948
1949
1950 ordered_weight_label_list = ['central','stat_error']
1951 for weight_label in weight_label_list:
1952 if not isinstance(weight_label, str):
1953 ordered_weight_label_list.append(weight_label)
1954 for weight_label in weight_label_list:
1955 if weight_label in ['central','stat_error','boundary_xmin','boundary_xmax']:
1956 continue
1957 if isinstance(weight_label, str):
1958 ordered_weight_label_list.append(weight_label)
1959
1960
1961
1962 return self.retrieve_plots_from_XML_source(selected_run_node,
1963 selected_weights, ordered_weight_label_list, raw_labels=False)
1964
1967 """Given an XML node and the selected weights and their ordered list,
1968 import all histograms from the specified XML node."""
1969
1970
1971 for multiplicity_node in xml_node.getElementsByTagName("jethistograms"):
1972 multiplicity = int(multiplicity_node.getAttribute('njet'))
1973 for histogram in multiplicity_node.getElementsByTagName("histogram"):
1974
1975 if histogram.getAttribute("weight")!='all':
1976 continue
1977 new_histo = HwU()
1978 hist_name = str(histogram.getAttribute('name'))
1979
1980 new_histo.process_histogram_name('%s |JETSAMPLE@%d'%(hist_name,multiplicity))
1981
1982
1983 if new_histo.type == 'AUX':
1984 continue
1985
1986
1987
1988 new_histo.bins = BinList(weight_labels = ordered_weight_label_list)
1989 hist_data = str(histogram.childNodes[0].data)
1990 for line in hist_data.split('\n'):
1991 if line.strip()=='':
1992 continue
1993 bin_weights = {}
1994 boundaries = [0.0,0.0]
1995 for j, weight in \
1996 enumerate(HwU.histo_bin_weight_re.finditer(line)):
1997 try:
1998 for wgt_label in selected_weights[j]:
1999 if wgt_label == 'boundary_xmin':
2000 boundaries[0] = float(weight.group('weight'))
2001 elif wgt_label == 'boundary_xmax':
2002 boundaries[1] = float(weight.group('weight'))
2003 else:
2004 if weight.group('weight').upper()=='NAN':
2005 raise MadGraph5Error, \
2006 "Some weights are found to be 'NAN' in histogram with name '%s'"%hist_name+\
2007 " and jet sample multiplicity %d."%multiplicity
2008 else:
2009 bin_weights[wgt_label] = \
2010 float(weight.group('weight'))
2011 except KeyError:
2012 continue
2013
2014 if len(bin_weights)!=len(ordered_weight_label_list):
2015 raise MadGraph5Error, \
2016 'Not all defined weights were found in the XML source.\n'+\
2017 '%d found / %d expected.'%(len(bin_weights),len(ordered_weight_label_list))+\
2018 '\nThe missing ones are: %s.'%\
2019 str(list(set(ordered_weight_label_list)-set(bin_weights.keys())))+\
2020 "\nIn plot with title '%s' and jet sample multiplicity %d."%\
2021 (hist_name, multiplicity)
2022
2023 new_histo.bins.append(Bin(tuple(boundaries), bin_weights))
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040 if not raw_labels:
2041 new_histo.trim_auxiliary_weights()
2042
2043
2044 self.append(new_histo)
2045
2046 - def output(self, path, format='gnuplot',number_of_ratios = -1,
2047 uncertainties=['scale','pdf','statitistical','merging_scale','alpsfact'],
2048 use_band = None,
2049 ratio_correlations=True, arg_string='',
2050 jet_samples_to_keep=None,
2051 auto_open=True,
2052 lhapdfconfig='lhapdf-config'):
2053 """ Ouput this histogram to a file, stream or string if path is kept to
2054 None. The supported format are for now. Chose whether to print the header
2055 or not."""
2056
2057 if len(self)==0:
2058 return MadGraph5Error, 'No histograms stored in the list yet.'
2059
2060 if not format in HwU.output_formats_implemented:
2061 raise MadGraph5Error, "The specified output format '%s'"%format+\
2062 " is not yet supported. Supported formats are %s."\
2063 %HwU.output_formats_implemented
2064
2065 if isinstance(path, str) and '.' not in os.path.basename(path):
2066 output_base_name = os.path.basename(path)
2067 HwU_stream = open(path+'.HwU','w')
2068 else:
2069 raise MadGraph5Error, "The path argument of the output function of"+\
2070 " the HwUList instance must be file path without its extension."
2071
2072 HwU_output_list = []
2073
2074
2075 if format == 'HwU':
2076 HwU_output_list.extend(self[0].get_HwU_source(print_header=True))
2077 for histo in self[1:]:
2078 HwU_output_list.extend(histo.get_HwU_source())
2079 HwU_output_list.extend(['',''])
2080 HwU_stream.write('\n'.join(HwU_output_list))
2081 HwU_stream.close()
2082 return
2083
2084
2085 if format == 'gnuplot':
2086 gnuplot_stream = open(path+'.gnuplot','w')
2087
2088
2089 matching_histo_lists = HwUList([HwUList([self[0]])])
2090 for histo in self[1:]:
2091 matched = False
2092 for histo_list in matching_histo_lists:
2093 if histo.test_plot_compability(histo_list[0],
2094 consider_type=False, consider_unknown_weight_labels=True):
2095 histo_list.append(histo)
2096 matched = True
2097 break
2098 if not matched:
2099 matching_histo_lists.append(HwUList([histo]))
2100
2101 self[:] = matching_histo_lists
2102
2103
2104 gnuplot_output_list_v4 = [
2105 """
2106 ################################################################################
2107 #
2108 # This gnuplot file was generated by MadGraph5_aMC@NLO project, a program which
2109 # automatically generates Feynman diagrams and matrix elements for arbitrary
2110 # high-energy processes in the Standard Model and beyond. It also perform the
2111 # integration and/or generate events for these processes, at LO and NLO accuracy.
2112 #
2113 # For more information, visit madgraph.phys.ucl.ac.be and amcatnlo.web.cern.ch
2114 #
2115 ################################################################################
2116 # %s
2117 reset
2118
2119 set lmargin 10
2120 set rmargin 0
2121 set terminal postscript portrait enhanced mono dashed lw 1.0 "Helvetica" 9
2122 # The pdf terminal offers transparency support, but you will have to adapt things a bit
2123 #set terminal pdf enhanced font "Helvetica 12" lw 1.0 dashed size 29.7cm, 21cm
2124 set key font ",9"
2125 set key samplen "2"
2126 set output "%s.ps"
2127
2128 # This is the "PODO" color palette of gnuplot v.5, but with the order
2129 # changed: palette of colors selected to be easily distinguishable by
2130 # color-blind individuals with either protanopia or deuteranopia. Bang
2131 # Wong [2011] Nature Methods 8, 441.
2132
2133 set style line 1 lt 1 lc rgb "#009e73" lw 2.5
2134 set style line 11 lt 2 lc rgb "#009e73" lw 2.5
2135 set style line 21 lt 4 lc rgb "#009e73" lw 2.5
2136 set style line 31 lt 6 lc rgb "#009e73" lw 2.5
2137 set style line 41 lt 8 lc rgb "#009e73" lw 2.5
2138
2139 set style line 2 lt 1 lc rgb "#0072b2" lw 2.5
2140 set style line 12 lt 2 lc rgb "#0072b2" lw 2.5
2141 set style line 22 lt 4 lc rgb "#0072b2" lw 2.5
2142 set style line 32 lt 6 lc rgb "#0072b2" lw 2.5
2143 set style line 42 lt 8 lc rgb "#0072b2" lw 2.5
2144
2145 set style line 3 lt 1 lc rgb "#d55e00" lw 2.5
2146 set style line 13 lt 2 lc rgb "#d55e00" lw 2.5
2147 set style line 23 lt 4 lc rgb "#d55e00" lw 2.5
2148 set style line 33 lt 6 lc rgb "#d55e00" lw 2.5
2149 set style line 43 lt 8 lc rgb "#d55e00" lw 2.5
2150
2151 set style line 4 lt 1 lc rgb "#f0e442" lw 2.5
2152 set style line 14 lt 2 lc rgb "#f0e442" lw 2.5
2153 set style line 24 lt 4 lc rgb "#f0e442" lw 2.5
2154 set style line 34 lt 6 lc rgb "#f0e442" lw 2.5
2155 set style line 44 lt 8 lc rgb "#f0e442" lw 2.5
2156
2157 set style line 5 lt 1 lc rgb "#56b4e9" lw 2.5
2158 set style line 15 lt 2 lc rgb "#56b4e9" lw 2.5
2159 set style line 25 lt 4 lc rgb "#56b4e9" lw 2.5
2160 set style line 35 lt 6 lc rgb "#56b4e9" lw 2.5
2161 set style line 45 lt 8 lc rgb "#56b4e9" lw 2.5
2162
2163 set style line 6 lt 1 lc rgb "#cc79a7" lw 2.5
2164 set style line 16 lt 2 lc rgb "#cc79a7" lw 2.5
2165 set style line 26 lt 4 lc rgb "#cc79a7" lw 2.5
2166 set style line 36 lt 6 lc rgb "#cc79a7" lw 2.5
2167 set style line 46 lt 8 lc rgb "#cc79a7" lw 2.5
2168
2169 set style line 7 lt 1 lc rgb "#e69f00" lw 2.5
2170 set style line 17 lt 2 lc rgb "#e69f00" lw 2.5
2171 set style line 27 lt 4 lc rgb "#e69f00" lw 2.5
2172 set style line 37 lt 6 lc rgb "#e69f00" lw 2.5
2173 set style line 47 lt 8 lc rgb "#e69f00" lw 2.5
2174
2175 set style line 8 lt 1 lc rgb "black" lw 2.5
2176 set style line 18 lt 2 lc rgb "black" lw 2.5
2177 set style line 28 lt 4 lc rgb "black" lw 2.5
2178 set style line 38 lt 6 lc rgb "black" lw 2.5
2179 set style line 48 lt 7 lc rgb "black" lw 2.5
2180
2181
2182 set style line 999 lt 1 lc rgb "gray" lw 2.5
2183
2184 safe(x,y,a) = (y == 0.0 ? a : x/y)
2185
2186 set style data histeps
2187 set key invert
2188
2189 """%(arg_string,output_base_name)
2190 ]
2191
2192 gnuplot_output_list_v5 = [
2193 """
2194 ################################################################################
2195 #
2196 # This gnuplot file was generated by MadGraph5_aMC@NLO project, a program which
2197 # automatically generates Feynman diagrams and matrix elements for arbitrary
2198 # high-energy processes in the Standard Model and beyond. It also perform the
2199 # integration and/or generate events for these processes, at LO and NLO accuracy.
2200 #
2201 # For more information, visit madgraph.phys.ucl.ac.be and amcatnlo.web.cern.ch
2202 #
2203 ################################################################################
2204 # %s
2205 reset
2206
2207 set lmargin 10
2208 set rmargin 0
2209 set terminal postscript portrait enhanced color "Helvetica" 9
2210 # The pdf terminal offers transparency support, but you will have to adapt things a bit
2211 #set terminal pdf enhanced font "Helvetica 12" lw 1.0 dashed size 29.7cm, 21cm
2212 set key font ",9"
2213 set key samplen "2"
2214 set output "%s.ps"
2215
2216 # This is the "PODO" color palette of gnuplot v.5, but with the order
2217 # changed: palette of colors selected to be easily distinguishable by
2218 # color-blind individuals with either protanopia or deuteranopia. Bang
2219 # Wong [2011] Nature Methods 8, 441.
2220
2221 set style line 1 lt 1 lc rgb "#009e73" lw 1.3
2222 set style line 101 lt 1 lc rgb "#009e73" lw 1.3 dt (6,3)
2223 set style line 11 lt 2 lc rgb "#009e73" lw 1.3 dt (6,3)
2224 set style line 21 lt 4 lc rgb "#009e73" lw 1.3 dt (3,2)
2225 set style line 31 lt 6 lc rgb "#009e73" lw 1.3 dt (2,1)
2226 set style line 41 lt 8 lc rgb "#009e73" lw 1.3 dt (4,3)
2227
2228 set style line 2 lt 1 lc rgb "#0072b2" lw 1.3
2229 set style line 102 lt 1 lc rgb "#0072b2" lw 1.3 dt (6,3)
2230 set style line 12 lt 2 lc rgb "#0072b2" lw 1.3 dt (6,3)
2231 set style line 22 lt 4 lc rgb "#0072b2" lw 1.3 dt (3,2)
2232 set style line 32 lt 6 lc rgb "#0072b2" lw 1.3 dt (2,1)
2233 set style line 42 lt 8 lc rgb "#0072b2" lw 1.3 dt (4,3)
2234
2235
2236 set style line 3 lt 1 lc rgb "#d55e00" lw 1.3
2237 set style line 103 lt 1 lc rgb "#d55e00" lw 1.3 dt (6,3)
2238 set style line 13 lt 2 lc rgb "#d55e00" lw 1.3 dt (6,3)
2239 set style line 23 lt 4 lc rgb "#d55e00" lw 1.3 dt (3,2)
2240 set style line 33 lt 6 lc rgb "#d55e00" lw 1.3 dt (2,1)
2241 set style line 43 lt 8 lc rgb "#d55e00" lw 1.3 dt (4,3)
2242
2243 set style line 4 lt 1 lc rgb "#f0e442" lw 1.3
2244 set style line 104 lt 1 lc rgb "#f0e442" lw 1.3 dt (6,3)
2245 set style line 14 lt 2 lc rgb "#f0e442" lw 1.3 dt (6,3)
2246 set style line 24 lt 4 lc rgb "#f0e442" lw 1.3 dt (3,2)
2247 set style line 34 lt 6 lc rgb "#f0e442" lw 1.3 dt (2,1)
2248 set style line 44 lt 8 lc rgb "#f0e442" lw 1.3 dt (4,3)
2249
2250 set style line 5 lt 1 lc rgb "#56b4e9" lw 1.3
2251 set style line 105 lt 1 lc rgb "#56b4e9" lw 1.3 dt (6,3)
2252 set style line 15 lt 2 lc rgb "#56b4e9" lw 1.3 dt (6,3)
2253 set style line 25 lt 4 lc rgb "#56b4e9" lw 1.3 dt (3,2)
2254 set style line 35 lt 6 lc rgb "#56b4e9" lw 1.3 dt (2,1)
2255 set style line 45 lt 8 lc rgb "#56b4e9" lw 1.3 dt (4,3)
2256
2257 set style line 6 lt 1 lc rgb "#cc79a7" lw 1.3
2258 set style line 106 lt 1 lc rgb "#cc79a7" lw 1.3 dt (6,3)
2259 set style line 16 lt 2 lc rgb "#cc79a7" lw 1.3 dt (6,3)
2260 set style line 26 lt 4 lc rgb "#cc79a7" lw 1.3 dt (3,2)
2261 set style line 36 lt 6 lc rgb "#cc79a7" lw 1.3 dt (2,1)
2262 set style line 46 lt 8 lc rgb "#cc79a7" lw 1.3 dt (4,3)
2263
2264 set style line 7 lt 1 lc rgb "#e69f00" lw 1.3
2265 set style line 107 lt 1 lc rgb "#e69f00" lw 1.3 dt (6,3)
2266 set style line 17 lt 2 lc rgb "#e69f00" lw 1.3 dt (6,3)
2267 set style line 27 lt 4 lc rgb "#e69f00" lw 1.3 dt (3,2)
2268 set style line 37 lt 6 lc rgb "#e69f00" lw 1.3 dt (2,1)
2269 set style line 47 lt 8 lc rgb "#e69f00" lw 1.3 dt (4,3)
2270
2271 set style line 8 lt 1 lc rgb "black" lw 1.3
2272 set style line 108 lt 1 lc rgb "black" lw 1.3 dt (6,3)
2273 set style line 18 lt 2 lc rgb "black" lw 1.3 dt (6,3)
2274 set style line 28 lt 4 lc rgb "black" lw 1.3 dt (3,2)
2275 set style line 38 lt 6 lc rgb "black" lw 1.3 dt (2,1)
2276 set style line 48 lt 8 lc rgb "black" lw 1.3 dt (4,3)
2277
2278
2279 set style line 999 lt 1 lc rgb "gray" lw 1.3
2280
2281 safe(x,y,a) = (y == 0.0 ? a : x/y)
2282
2283 set style data histeps
2284 set key invert
2285
2286 """%(arg_string,output_base_name)
2287 ]
2288
2289
2290 try:
2291 p = subprocess.Popen(['gnuplot', '--version'], \
2292 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
2293 except OSError:
2294
2295
2296 gnuplot_output_list=gnuplot_output_list_v4
2297 else:
2298 output, _ = p.communicate()
2299 if float(output.split()[1]) < 5. :
2300 gnuplot_output_list=gnuplot_output_list_v4
2301 else:
2302 gnuplot_output_list=gnuplot_output_list_v5
2303
2304
2305
2306
2307 block_position = 0
2308 for histo_group in self:
2309
2310 block_position = histo_group.output_group(HwU_output_list,
2311 gnuplot_output_list, block_position,output_base_name+'.HwU',
2312 number_of_ratios=number_of_ratios,
2313 uncertainties = uncertainties,
2314 use_band = use_band,
2315 ratio_correlations = ratio_correlations,
2316 jet_samples_to_keep=jet_samples_to_keep,
2317 lhapdfconfig = lhapdfconfig)
2318
2319
2320 gnuplot_output_list.extend([
2321 "unset multiplot",
2322 '!ps2pdf "%s.ps" &> /dev/null'%output_base_name])
2323 if auto_open:
2324 gnuplot_output_list.append(
2325 '!open "%s.pdf" &> /dev/null'%output_base_name)
2326
2327
2328 gnuplot_stream.write('\n'.join(gnuplot_output_list))
2329 HwU_stream.write('\n'.join(HwU_output_list))
2330 gnuplot_stream.close()
2331 HwU_stream.close()
2332
2333 logger.debug("Histograms have been written out at "+\
2334 "%s.[HwU|gnuplot]' and can "%output_base_name+\
2335 "now be rendered by invoking gnuplot.")
2336
2337 - def output_group(self, HwU_out, gnuplot_out, block_position, HwU_name,
2338 number_of_ratios = -1,
2339 uncertainties = ['scale','pdf','statitistical','merging_scale','alpsfact'],
2340 use_band = None,
2341 ratio_correlations = True,
2342 jet_samples_to_keep=None,
2343 lhapdfconfig='lhapdf-config'):
2344
2345 """ This functions output a single group of histograms with either one
2346 histograms untyped (i.e. type=None) or two of type 'NLO' and 'LO'
2347 respectively."""
2348
2349
2350
2351 def get_main_central_plot_lines(HwU_name, block_position, color_index,
2352 title, show_mc_uncertainties):
2353 """ Returns two plot lines, one for the negative contributions in
2354 dashed and one with the positive ones in solid."""
2355
2356 template = "'%(hwu)s' index %(ind)d using (($1+$2)/2):%(data)s%(stat_col)s%(stat_err)s%(ls)s%(title)s"
2357 template_no_stat = "'%(hwu)s' index %(ind)d using (($1+$2)/2):%(data)s%(ls)s%(title)s"
2358 rep_dic = {'hwu':HwU_name,
2359 'ind':block_position,
2360 'ls':' ls %d'%color_index,
2361 'title':" title '%s'"%title,
2362 'stat_col': ':4',
2363 'stat_err': ' w yerrorbar',
2364 'data':'3',
2365 'linetype':''}
2366
2367
2368
2369
2370
2371
2372 res = []
2373 rep_dic['data'] = '($3 < 0 ? sqrt(-1) : $3)'
2374 res.append(template_no_stat%rep_dic)
2375 rep_dic['title'] = " title ''"
2376 if show_mc_uncertainties:
2377 res.append(template%rep_dic)
2378 rep_dic['data'] = '($3 >= 0 ? sqrt(-1) : abs($3))'
2379 rep_dic['ls'] = ' ls %d'%(100+color_index)
2380 res.append(template_no_stat%rep_dic)
2381 if show_mc_uncertainties:
2382 res.append(template%rep_dic)
2383 return res
2384
2385
2386
2387
2388 def get_uncertainty_lines(HwU_name, block_position,
2389 var_pos, color_index,title, ratio=False, band=False):
2390 """ Return a string line corresponding to the plotting of the
2391 uncertainty. Band is to chose wether to display uncertainty with
2392 a band or two lines."""
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412 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'"
2413
2414
2415 copy_swap_re = copy_swap_re.replace('\\','\\\\')
2416
2417 position = '(safe($%d,$3,1.0)-1.0)' if ratio else '%d'
2418 if not band:
2419 return ["'%s' index %d using (($1+$2)/2):%s ls %d title '%s'"\
2420 %(HwU_name,block_position, position%(var_pos),color_index,title),
2421 "'%s' index %d using (($1+$2)/2):%s ls %d title ''"\
2422 %(HwU_name,block_position, position%(var_pos+1),color_index)]
2423 else:
2424 return [' "<%s %s" index %d using 1:%s:%s with filledcurve ls %d fs transparent solid 0.2 title \'%s\''%\
2425 (copy_swap_re,HwU_name,block_position,
2426 position%var_pos,position%(var_pos+1),color_index,title)]
2427
2428
2429
2430 layout_geometry = [(0.0, 0.5, 1.0, 0.4 ),
2431 (0.0, 0.35, 1.0, 0.15),
2432 (0.0, 0.2, 1.0, 0.15)]
2433 layout_geometry.reverse()
2434
2435
2436
2437 matching_histo_lists = HwUList([HwUList([self[0]])])
2438 for histo in self[1:]:
2439 matched = False
2440 for histo_list in matching_histo_lists:
2441 if hasattr(histo, 'jetsample') and histo.jetsample >= 0 and \
2442 histo.type == histo_list[0].type:
2443 matched = True
2444 histo_list.append(histo)
2445 break
2446 if not matched:
2447 matching_histo_lists.append(HwUList([histo]))
2448
2449
2450
2451 self[:] = []
2452 for histo_group in matching_histo_lists:
2453
2454
2455 if len(histo_group)==1:
2456 self.append(histo_group[0])
2457 continue
2458
2459
2460 if any(hist.jetsample==-1 for hist in histo_group if
2461 hasattr(hist, 'jetsample')):
2462 self.extend(histo_group)
2463 continue
2464 summed_histogram = copy.copy(histo_group[0])
2465 for histo in histo_group[1:]:
2466 summed_histogram = summed_histogram + histo
2467 summed_histogram.jetsample = -1
2468 self.append(summed_histogram)
2469 self.extend(histo_group)
2470
2471
2472 if not jet_samples_to_keep is None:
2473 self[:] = filter(lambda histo:
2474 (not hasattr(histo,'jetsample')) or (histo.jetsample == -1) or
2475 (histo.jetsample in jet_samples_to_keep), self)
2476
2477
2478
2479 def ratio_no_correlations(wgtsA, wgtsB):
2480 new_wgts = {}
2481 for label, wgt in wgtsA.items():
2482 if wgtsB['central']==0.0 and wgt==0.0:
2483 new_wgts[label] = 0.0
2484 continue
2485 elif wgtsB['central']==0.0:
2486
2487
2488
2489 new_wgts[label] = 0.0
2490 continue
2491 new_wgts[label] = (wgtsA[label]/wgtsB['central'])
2492 return new_wgts
2493
2494
2495
2496 n_histograms = len(self)
2497 ratio_histos = HwUList([])
2498
2499 n_ratios_included = 0
2500 for i, histo in enumerate(self[1:]):
2501 if not hasattr(histo,'jetsample') or histo.jetsample==self[0].jetsample:
2502 n_ratios_included += 1
2503 else:
2504 continue
2505
2506 if number_of_ratios >=0 and n_ratios_included > number_of_ratios:
2507 break
2508
2509 if ratio_correlations:
2510 ratio_histos.append(histo/self[0])
2511 else:
2512 ratio_histos.append(self[0].__class__.combine(histo, self[0],
2513 ratio_no_correlations))
2514 if self[0].type=='NLO' and self[1].type=='LO':
2515 ratio_histos[-1].title += '1/K-factor'
2516 elif self[0].type=='LO' and self[1].type=='NLO':
2517 ratio_histos[-1].title += 'K-factor'
2518 else:
2519 ratio_histos[-1].title += ' %s/%s'%(
2520 self[1].type if self[1].type else '(%d)'%(i+2),
2521 self[0].type if self[0].type else '(1)')
2522
2523
2524 ratio_histos[-1].type = 'AUX'
2525 self.extend(ratio_histos)
2526
2527
2528 if 'scale' in uncertainties:
2529 (mu_var_pos,mu) = self[0].set_uncertainty(type='all_scale')
2530 else:
2531 (mu_var_pos,mu) = (None,[None])
2532
2533 if 'pdf' in uncertainties:
2534 (PDF_var_pos,pdf) = self[0].set_uncertainty(type='PDF',lhapdfconfig=lhapdfconfig)
2535 else:
2536 (PDF_var_pos,pdf) = (None,[None])
2537
2538 if 'merging_scale' in uncertainties:
2539 (merging_var_pos,merging) = self[0].set_uncertainty(type='merging')
2540 else:
2541 (merging_var_pos,merging) = (None,[None])
2542 if 'alpsfact' in uncertainties:
2543 (alpsfact_var_pos,alpsfact) = self[0].set_uncertainty(type='alpsfact')
2544 else:
2545 (alpsfact_var_pos,alpsfact) = (None,[None])
2546
2547 uncertainties_present = list(uncertainties)
2548 if PDF_var_pos is None and 'pdf' in uncertainties_present:
2549 uncertainties_present.remove('pdf')
2550 if mu_var_pos is None and 'scale' in uncertainties_present:
2551 uncertainties_present.remove('scale')
2552 if merging_var_pos is None and 'merging' in uncertainties_present:
2553 uncertainties_present.remove('merging')
2554 if alpsfact_var_pos is None and 'alpsfact' in uncertainties_present:
2555 uncertainties_present.remove('alpsfact')
2556 no_uncertainties = len(uncertainties_present)==0
2557
2558
2559 try:
2560 uncertainties_present.remove('statistical')
2561 except:
2562 pass
2563 if use_band is None:
2564
2565
2566 if len(uncertainties_present)==0:
2567 use_band = []
2568 elif len(uncertainties_present)==1:
2569 use_band = uncertainties_present
2570 elif 'scale' in uncertainties_present:
2571 use_band = ['scale']
2572 else:
2573 use_band = [uncertainties_present[0]]
2574
2575 for histo in self[1:]:
2576 if (not mu_var_pos is None) and \
2577 mu_var_pos != histo.set_uncertainty(type='all_scale')[0]:
2578 raise MadGraph5Error, 'Not all histograms in this group specify'+\
2579 ' scale uncertainties. It is required to be able to output them'+\
2580 ' together.'
2581 if (not PDF_var_pos is None) and\
2582 PDF_var_pos != histo.set_uncertainty(type='PDF',\
2583 lhapdfconfig=lhapdfconfig)[0]:
2584 raise MadGraph5Error, 'Not all histograms in this group specify'+\
2585 ' PDF uncertainties. It is required to be able to output them'+\
2586 ' together.'
2587 if (not merging_var_pos is None) and\
2588 merging_var_pos != histo.set_uncertainty(type='merging')[0]:
2589 raise MadGraph5Error, 'Not all histograms in this group specify'+\
2590 ' merging uncertainties. It is required to be able to output them'+\
2591 ' together.'
2592 if (not alpsfact_var_pos is None) and\
2593 alpsfact_var_pos != histo.set_uncertainty(type='alpsfact')[0]:
2594 raise MadGraph5Error, 'Not all histograms in this group specify'+\
2595 ' alpsfact uncertainties. It is required to be able to output them'+\
2596 ' together.'
2597
2598
2599
2600 for i, histo in enumerate(self):
2601
2602 HwU_out.extend(histo.get_HwU_source(\
2603 print_header=(block_position==0 and i==0)))
2604 HwU_out.extend(['',''])
2605
2606
2607 global_header =\
2608 """
2609 ################################################################################
2610 ### Rendering of the plot titled '%(title)s'
2611 ################################################################################
2612
2613 set multiplot
2614 set label "%(title)s" font ",13" at graph 0.04, graph 1.05
2615 set xrange [%(xmin).4e:%(xmax).4e]
2616 set bmargin 0
2617 set tmargin 0
2618 set xtics nomirror
2619 set ytics nomirror
2620 set mytics %(mxtics)d
2621 %(set_xtics)s
2622 set key horizontal noreverse maxcols 1 width -4
2623 set label front 'MadGraph5\_aMC\@NLO' font "Courier,11" rotate by 90 at graph 1.02, graph 0.04
2624 """
2625
2626
2627 subhistogram_header = \
2628 """#-- rendering subhistograms '%(subhistogram_type)s'
2629 %(unset label)s
2630 %(set_format_y)s
2631 set yrange [%(ymin).4e:%(ymax).4e]
2632 set origin %(origin_x).4e, %(origin_y).4e
2633 set size %(size_x).4e, %(size_y).4e
2634 set mytics %(mytics)d
2635 %(set_ytics)s
2636 %(set_format_x)s
2637 %(set_yscale)s
2638 %(set_ylabel)s
2639 %(set_histo_label)s
2640 plot \\"""
2641 replacement_dic = {}
2642
2643 replacement_dic['title'] = self[0].get_HwU_histogram_name(format='human-no_type')
2644
2645
2646 wgts_to_consider = ['central']
2647 if not mu_var_pos is None:
2648 for mu_var in mu_var_pos:
2649 wgts_to_consider.append(self[0].bins.weight_labels[mu_var])
2650 wgts_to_consider.append(self[0].bins.weight_labels[mu_var+1])
2651 wgts_to_consider.append(self[0].bins.weight_labels[mu_var+2])
2652 if not PDF_var_pos is None:
2653 for PDF_var in PDF_var_pos:
2654 wgts_to_consider.append(self[0].bins.weight_labels[PDF_var])
2655 wgts_to_consider.append(self[0].bins.weight_labels[PDF_var+1])
2656 wgts_to_consider.append(self[0].bins.weight_labels[PDF_var+2])
2657 if not merging_var_pos is None:
2658 for merging_var in merging_var_pos:
2659 wgts_to_consider.append(self[0].bins.weight_labels[merging_var])
2660 wgts_to_consider.append(self[0].bins.weight_labels[merging_var+1])
2661 wgts_to_consider.append(self[0].bins.weight_labels[merging_var+2])
2662 if not alpsfact_var_pos is None:
2663 for alpsfact_var in alpsfact_var_pos:
2664 wgts_to_consider.append(self[0].bins.weight_labels[alpsfact_var])
2665 wgts_to_consider.append(self[0].bins.weight_labels[alpsfact_var+1])
2666 wgts_to_consider.append(self[0].bins.weight_labels[alpsfact_var+2])
2667
2668 (xmin, xmax) = HwU.get_x_optimal_range(self[:2],\
2669 weight_labels = wgts_to_consider)
2670 replacement_dic['xmin'] = xmin
2671 replacement_dic['xmax'] = xmax
2672 replacement_dic['mxtics'] = 10
2673 replacement_dic['set_xtics'] = 'set xtics auto'
2674
2675
2676 gnuplot_out.append(global_header%replacement_dic)
2677
2678
2679 replacement_dic['subhistogram_type'] = '%s and %s results'%(
2680 str(self[0].type),str(self[1].type)) if len(self)>1 else \
2681 'single diagram output'
2682 (ymin, ymax) = HwU.get_y_optimal_range(self[:2],
2683 labels = wgts_to_consider, scale=self[0].y_axis_mode)
2684
2685
2686 if ymin< 0.0:
2687 self[0].y_axis_mode = 'LIN'
2688
2689
2690 if self[0].y_axis_mode=='LOG':
2691 ymax += 10.0 * ymax
2692 ymin -= 0.1 * ymin
2693 else:
2694 ymax += 0.3 * (ymax - ymin)
2695 ymin -= 0.3 * (ymax - ymin)
2696
2697 replacement_dic['ymin'] = ymin
2698 replacement_dic['ymax'] = ymax
2699 replacement_dic['unset label'] = ''
2700 (replacement_dic['origin_x'], replacement_dic['origin_y'],
2701 replacement_dic['size_x'], replacement_dic['size_y']) = layout_geometry.pop()
2702 replacement_dic['mytics'] = 10
2703
2704 replacement_dic['set_ytics'] = 'set ytics auto'
2705 replacement_dic['set_format_x'] = "set format x ''" if \
2706 (len(self)-n_histograms>0 or not no_uncertainties) else "set format x"
2707 replacement_dic['set_ylabel'] = 'set ylabel "{/Symbol s} per bin [pb]"'
2708 replacement_dic['set_yscale'] = "set logscale y" if \
2709 self[0].y_axis_mode=='LOG' else 'unset logscale y'
2710 replacement_dic['set_format_y'] = "set format y '10^{%T}'" if \
2711 self[0].y_axis_mode=='LOG' else 'unset format'
2712
2713 replacement_dic['set_histo_label'] = ""
2714 gnuplot_out.append(subhistogram_header%replacement_dic)
2715
2716
2717 plot_lines = []
2718 uncertainty_plot_lines = []
2719 n=-1
2720
2721 for i, histo in enumerate(self[:n_histograms]):
2722 n=n+1
2723 color_index = n%self.number_line_colors_defined+1
2724
2725 title = []
2726 if histo.type is None and not hasattr(histo, 'jetsample'):
2727 title.append('%d'%(i+1))
2728 else:
2729 if histo.type:
2730 title.append('NLO' if \
2731 histo.type.split()[0]=='NLO' else histo.type)
2732 if hasattr(histo, 'jetsample'):
2733 if histo.jetsample!=-1:
2734 title.append('jet sample %d'%histo.jetsample)
2735 else:
2736 title.append('all jet samples')
2737
2738 title = ', '.join(title)
2739
2740 if histo.type is None and not hasattr(histo, 'jetsample'):
2741 major_title = 'central value for plot (%d)'%(i+1)
2742 else:
2743 major_title = []
2744 if not histo.type is None:
2745 major_title.append(histo.type)
2746 if hasattr(histo, 'jetsample'):
2747 if histo.jetsample!=-1:
2748 major_title.append('jet sample %d'%histo.jetsample)
2749 else:
2750 major_title.append('all jet samples')
2751 else:
2752 major_title.append('central value')
2753 major_title = ', '.join(major_title)
2754
2755 if not mu[0] in ['none',None]:
2756 major_title += ', dynamical\_scale\_choice=%s'%mu[0]
2757 if not pdf[0] in ['none',None]:
2758 major_title += ', PDF=%s'%pdf[0].replace('_','\_')
2759
2760
2761
2762 if not (i!=0 and hasattr(histo,'jetsample') and histo.jetsample!=-1 and \
2763 not (jet_samples_to_keep and len(jet_samples_to_keep)==1 and
2764 jet_samples_to_keep[0] == histo.jetsample)):
2765
2766 uncertainty_plot_lines.append({})
2767
2768
2769
2770
2771
2772
2773
2774
2775 if not mu_var_pos is None and len(mu_var_pos)>0:
2776 if 'scale' in use_band:
2777 uncertainty_plot_lines[-1]['scale'] = get_uncertainty_lines(
2778 HwU_name, block_position+i, mu_var_pos[0]+4, color_index+10,
2779 '%s, scale variation'%title, band='scale' in use_band)
2780 else:
2781 uncertainty_plot_lines[-1]['scale'] = \
2782 ["sqrt(-1) ls %d title '%s'"%(color_index+10,'%s, scale variation'%title)]
2783
2784 if not PDF_var_pos is None and len(PDF_var_pos)>0:
2785 if 'pdf' in use_band:
2786 uncertainty_plot_lines[-1]['pdf'] = get_uncertainty_lines(
2787 HwU_name,block_position+i, PDF_var_pos[0]+4, color_index+20,
2788 '%s, PDF variation'%title, band='pdf' in use_band)
2789 else:
2790 uncertainty_plot_lines[-1]['pdf'] = \
2791 ["sqrt(-1) ls %d title '%s'"%(color_index+20,'%s, PDF variation'%title)]
2792
2793 if not merging_var_pos is None and len(merging_var_pos)>0:
2794 if 'merging_scale' in use_band:
2795 uncertainty_plot_lines[-1]['merging_scale'] = get_uncertainty_lines(
2796 HwU_name,block_position+i, merging_var_pos[0]+4, color_index+30,
2797 '%s, merging scale variation'%title, band='merging_scale' in use_band)
2798 else:
2799 uncertainty_plot_lines[-1]['merging_scale'] = \
2800 ["sqrt(-1) ls %d title '%s'"%(color_index+30,'%s, merging scale variation'%title)]
2801
2802 if not alpsfact_var_pos is None and len(alpsfact_var_pos)>0:
2803 if 'alpsfact' in use_band:
2804 uncertainty_plot_lines[-1]['alpsfact'] = get_uncertainty_lines(
2805 HwU_name,block_position+i, alpsfact_var_pos[0]+4, color_index+40,
2806 '%s, alpsfact variation'%title, band='alpsfact' in use_band)
2807 else:
2808 uncertainty_plot_lines[-1]['alpsfact'] = \
2809 ["sqrt(-1) ls %d title '%s'"%(color_index+40,'%s, alpsfact variation'%title)]
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819 plot_lines.extend(
2820 get_main_central_plot_lines(HwU_name, block_position+i,
2821 color_index, major_title, 'statistical' in uncertainties))
2822
2823
2824 if not mu_var_pos is None:
2825 for j,mu_var in enumerate(mu_var_pos):
2826 if j!=0:
2827 n=n+1
2828 color_index = n%self.number_line_colors_defined+1
2829 plot_lines.append(
2830 "'%s' index %d using (($1+$2)/2):%d ls %d title '%s'"\
2831 %(HwU_name,block_position+i,mu_var+3,color_index,\
2832 '%s dynamical\_scale\_choice=%s' % (title,mu[j])))
2833
2834 if not PDF_var_pos is None:
2835 for j,PDF_var in enumerate(PDF_var_pos):
2836 if j!=0:
2837 n=n+1
2838 color_index = n%self.number_line_colors_defined+1
2839 plot_lines.append(
2840 "'%s' index %d using (($1+$2)/2):%d ls %d title '%s'"\
2841 %(HwU_name,block_position+i,PDF_var+3,color_index,\
2842 '%s PDF=%s' % (title,pdf[j].replace('_','\_'))))
2843
2844
2845
2846 for one_plot in uncertainty_plot_lines:
2847 for uncertainty_type, lines in one_plot.items():
2848 if not uncertainty_type in use_band:
2849 plot_lines.extend(lines)
2850
2851 for one_plot in uncertainty_plot_lines:
2852 for uncertainty_type, lines in one_plot.items():
2853 if uncertainty_type in use_band:
2854 plot_lines.extend(lines)
2855
2856
2857 plot_lines.reverse()
2858
2859
2860 gnuplot_out.append(',\\\n'.join(plot_lines))
2861
2862
2863 replacement_dic['subhistogram_type'] = 'Relative scale and PDF uncertainty'
2864
2865 if 'statistical' in uncertainties:
2866 wgts_to_consider.append('stat_error')
2867
2868
2869
2870 def rel_scale(wgtsA, wgtsB):
2871 new_wgts = {}
2872 for label, wgt in wgtsA.items():
2873 if label in wgts_to_consider:
2874 if wgtsB['central']==0.0 and wgt==0.0:
2875 new_wgts[label] = 0.0
2876 continue
2877 elif wgtsB['central']==0.0:
2878
2879
2880
2881 new_wgts[label] = 0.0
2882 continue
2883 new_wgts[label] = (wgtsA[label]/wgtsB['central'])
2884 if label != 'stat_error':
2885 new_wgts[label] -= 1.0
2886 else:
2887 new_wgts[label] = wgtsA[label]
2888 return new_wgts
2889
2890 histos_for_subplots = [(i,histo) for i, histo in enumerate(self[:n_histograms]) if
2891 ( not (i!=0 and hasattr(histo,'jetsample') and histo.jetsample!=-1 and \
2892 not (jet_samples_to_keep and len(jet_samples_to_keep)==1 and
2893 jet_samples_to_keep[0] == histo.jetsample)) )]
2894
2895
2896
2897
2898 (ymin, ymax) = HwU.get_y_optimal_range([histo[1].__class__.combine(
2899 histo[1],histo[1],rel_scale) for histo in histos_for_subplots],
2900 labels = wgts_to_consider, scale='LIN')
2901
2902
2903 ymax = ymax + 0.2 * (ymax - ymin)
2904 ymin = ymin - 0.2 * (ymax - ymin)
2905 replacement_dic['unset label'] = 'unset label'
2906 replacement_dic['ymin'] = ymin
2907 replacement_dic['ymax'] = ymax
2908 if not no_uncertainties:
2909 (replacement_dic['origin_x'], replacement_dic['origin_y'],
2910 replacement_dic['size_x'], replacement_dic['size_y']) = layout_geometry.pop()
2911 replacement_dic['mytics'] = 2
2912
2913 replacement_dic['set_ytics'] = 'set ytics auto'
2914 replacement_dic['set_format_x'] = "set format x ''" if \
2915 len(self)-n_histograms>0 else "set format x"
2916 replacement_dic['set_ylabel'] = 'set ylabel "%s rel.unc."'\
2917 %('(1)' if self[0].type==None else '%s'%('NLO' if \
2918 self[0].type.split()[0]=='NLO' else self[0].type))
2919 replacement_dic['set_yscale'] = "unset logscale y"
2920 replacement_dic['set_format_y'] = 'unset format'
2921
2922
2923 tit='Relative uncertainties w.r.t. central value'
2924 if n_histograms > 1:
2925 tit=tit+'s'
2926
2927
2928
2929
2930 replacement_dic['set_histo_label'] = \
2931 'set label "%s" font ",9" front at graph 0.03, graph 0.13' % tit
2932
2933
2934 if not no_uncertainties:
2935 gnuplot_out.append(subhistogram_header%replacement_dic)
2936
2937
2938 plot_lines = []
2939 uncertainty_plot_lines = []
2940 n=-1
2941 for (i,histo) in histos_for_subplots:
2942 n=n+1
2943 k=n
2944 color_index = n%self.number_line_colors_defined+1
2945
2946 if not mu_var_pos is None:
2947 for j,mu_var in enumerate(mu_var_pos):
2948 uncertainty_plot_lines.append({})
2949 if j==0:
2950 color_index = k%self.number_line_colors_defined+1
2951 else:
2952 n=n+1
2953 color_index = n%self.number_line_colors_defined+1
2954
2955 if j>0 or mu[j]!='none':
2956 plot_lines.append(
2957 "'%s' index %d using (($1+$2)/2):(safe($%d,$3,1.0)-1.0) ls %d title ''"\
2958 %(HwU_name,block_position+i,mu_var+3,color_index))
2959 uncertainty_plot_lines[-1]['scale'] = get_uncertainty_lines(
2960 HwU_name, block_position+i, mu_var+4, color_index+10,'',
2961 ratio=True, band='scale' in use_band)
2962 if not PDF_var_pos is None:
2963 for j,PDF_var in enumerate(PDF_var_pos):
2964 uncertainty_plot_lines.append({})
2965 if j==0:
2966 color_index = k%self.number_line_colors_defined+1
2967 else:
2968 n=n+1
2969 color_index = n%self.number_line_colors_defined+1
2970
2971 if j>0 or pdf[j]!='none':
2972 plot_lines.append(
2973 "'%s' index %d using (($1+$2)/2):(safe($%d,$3,1.0)-1.0) ls %d title ''"\
2974 %(HwU_name,block_position+i,PDF_var+3,color_index))
2975 uncertainty_plot_lines[-1]['pdf'] = get_uncertainty_lines(
2976 HwU_name, block_position+i, PDF_var+4, color_index+20,'',
2977 ratio=True, band='pdf' in use_band)
2978 if not merging_var_pos is None:
2979 for j,merging_var in enumerate(merging_var_pos):
2980 uncertainty_plot_lines.append({})
2981 if j==0:
2982 color_index = k%self.number_line_colors_defined+1
2983 else:
2984 n=n+1
2985 color_index = n%self.number_line_colors_defined+1
2986 if j>0 or merging[j]!='none':
2987 plot_lines.append(
2988 "'%s' index %d using (($1+$2)/2):(safe($%d,$3,1.0)-1.0) ls %d title ''"\
2989 %(HwU_name,block_position+i,merging_var+3,color_index))
2990 uncertainty_plot_lines[-1]['merging_scale'] = get_uncertainty_lines(
2991 HwU_name, block_position+i, merging_var+4, color_index+30,'',
2992 ratio=True, band='merging_scale' in use_band)
2993 if not alpsfact_var_pos is None:
2994 for j,alpsfact_var in enumerate(alpsfact_var_pos):
2995 uncertainty_plot_lines.append({})
2996 if j==0:
2997 color_index = k%self.number_line_colors_defined+1
2998 else:
2999 n=n+1
3000 color_index = n%self.number_line_colors_defined+1
3001 if j>0 or alpsfact[j]!='none':
3002 plot_lines.append(
3003 "'%s' index %d using (($1+$2)/2):(safe($%d,$3,1.0)-1.0) ls %d title ''"\
3004 %(HwU_name,block_position+i,alpsfact_var+3,color_index))
3005 uncertainty_plot_lines[-1]['alpsfact'] = get_uncertainty_lines(
3006 HwU_name, block_position+i, alpsfact_var+4, color_index+40,'',
3007 ratio=True, band='alpsfact' in use_band)
3008
3009 if 'statistical' in uncertainties:
3010 plot_lines.append(
3011 "'%s' index %d using (($1+$2)/2):(0.0):(safe($4,$3,0.0)) w yerrorbar ls %d title ''"%\
3012 (HwU_name,block_position+i,color_index))
3013
3014 plot_lines.append("0.0 ls 999 title ''")
3015
3016
3017
3018 for one_plot in uncertainty_plot_lines:
3019 for uncertainty_type, lines in one_plot.items():
3020 if not uncertainty_type in use_band:
3021 plot_lines.extend(lines)
3022
3023 for one_plot in uncertainty_plot_lines:
3024 for uncertainty_type, lines in one_plot.items():
3025 if uncertainty_type in use_band:
3026 plot_lines.extend(lines)
3027
3028
3029 plot_lines.reverse()
3030
3031 if not no_uncertainties:
3032 gnuplot_out.append(',\\\n'.join(plot_lines))
3033
3034
3035 if len(self)-n_histograms==0:
3036
3037 gnuplot_out.extend(['','unset label','',
3038 '################################################################################'])
3039
3040 return block_position+len(self)
3041
3042
3043 ratio_name_long='('
3044 for i, histo in enumerate(self[:n_histograms]):
3045 if i==0: continue
3046 ratio_name_long+='%d'%(i+1) if histo.type is None else ('NLO' if \
3047 histo.type.split()[0]=='NLO' else histo.type)
3048 ratio_name_long+=')/'
3049 ratio_name_long+=('(1' if self[0].type==None else '(%s'%('NLO' if \
3050 self[0].type.split()[0]=='NLO' else self[0].type))+' central value)'
3051
3052 ratio_name_short = 'ratio w.r.t. '+('1' if self[0].type==None else '%s'%('NLO' if \
3053 self[0].type.split()[0]=='NLO' else self[0].type))
3054
3055 replacement_dic['subhistogram_type'] = '%s ratio'%ratio_name_long
3056 replacement_dic['set_ylabel'] = 'set ylabel "%s"'%ratio_name_short
3057
3058 (ymin, ymax) = HwU.get_y_optimal_range(self[n_histograms:],
3059 labels = wgts_to_consider, scale='LIN',Kratio = True)
3060
3061
3062 ymax = ymax + 0.2 * (ymax - ymin)
3063 ymin = ymin - 0.2 * (ymax - ymin)
3064 replacement_dic['unset label'] = 'unset label'
3065 replacement_dic['ymin'] = ymin
3066 replacement_dic['ymax'] = ymax
3067 (replacement_dic['origin_x'], replacement_dic['origin_y'],
3068 replacement_dic['size_x'], replacement_dic['size_y']) = layout_geometry.pop()
3069 replacement_dic['mytics'] = 2
3070
3071 replacement_dic['set_ytics'] = 'set ytics auto'
3072 replacement_dic['set_format_x'] = "set format x"
3073 replacement_dic['set_yscale'] = "unset logscale y"
3074 replacement_dic['set_format_y'] = 'unset format'
3075 replacement_dic['set_histo_label'] = \
3076 'set label "%s" font ",9" at graph 0.03, graph 0.13'%ratio_name_long
3077
3078 gnuplot_out.append(subhistogram_header%replacement_dic)
3079
3080 uncertainty_plot_lines = []
3081 plot_lines = []
3082
3083
3084 n=-1
3085 n=n+1
3086 if not mu_var_pos is None:
3087 for j,mu_var in enumerate(mu_var_pos):
3088 if j!=0: n=n+1
3089 if not PDF_var_pos is None:
3090 for j,PDF_var in enumerate(PDF_var_pos):
3091 if j!=0: n=n+1
3092 if not merging_var_pos is None:
3093 for j,merging_var in enumerate(merging_var_pos):
3094 if j!=0: n=n+1
3095 if not alpsfact_var_pos is None:
3096 for j,alpsfact_var in enumerate(alpsfact_var_pos):
3097 if j!=0: n=n+1
3098
3099 for i_histo_ratio, histo_ration in enumerate(self[n_histograms:]):
3100 n=n+1
3101 k=n
3102 block_ratio_pos = block_position+n_histograms+i_histo_ratio
3103 color_index = n%self.number_line_colors_defined+1
3104
3105 plot_lines.append(
3106 "'%s' index %d using (($1+$2)/2):3 ls %d title ''"%\
3107 (HwU_name,block_ratio_pos,color_index))
3108 if 'statistical' in uncertainties:
3109 plot_lines.append(
3110 "'%s' index %d using (($1+$2)/2):3:4 w yerrorbar ls %d title ''"%\
3111 (HwU_name,block_ratio_pos,color_index))
3112
3113
3114 if not mu_var_pos is None:
3115 for j,mu_var in enumerate(mu_var_pos):
3116 uncertainty_plot_lines.append({})
3117 if j==0:
3118 color_index = k%self.number_line_colors_defined+1
3119 else:
3120 n=n+1
3121 color_index = n%self.number_line_colors_defined+1
3122
3123 if j>0 or mu[j]!='none':
3124 plot_lines.append(
3125 "'%s' index %d using (($1+$2)/2):%d ls %d title ''"\
3126 %(HwU_name,block_ratio_pos,mu_var+3,color_index))
3127 uncertainty_plot_lines[-1]['scale'] = get_uncertainty_lines(
3128 HwU_name, block_ratio_pos, mu_var+4, color_index+10,'',
3129 band='scale' in use_band)
3130 if not PDF_var_pos is None:
3131 for j,PDF_var in enumerate(PDF_var_pos):
3132 uncertainty_plot_lines.append({})
3133 if j==0:
3134 color_index = k%self.number_line_colors_defined+1
3135 else:
3136 n=n+1
3137 color_index = n%self.number_line_colors_defined+1
3138
3139 if j>0 or pdf[j]!='none':
3140 plot_lines.append(
3141 "'%s' index %d using (($1+$2)/2):%d ls %d title ''"\
3142 %(HwU_name,block_ratio_pos,PDF_var+3,color_index))
3143 uncertainty_plot_lines[-1]['pdf'] = get_uncertainty_lines(
3144 HwU_name, block_ratio_pos, PDF_var+4, color_index+20,'',
3145 band='pdf' in use_band)
3146 if not merging_var_pos is None:
3147 for j,merging_var in enumerate(merging_var_pos):
3148 uncertainty_plot_lines.append({})
3149 if j==0:
3150 color_index = k%self.number_line_colors_defined+1
3151 else:
3152 n=n+1
3153 color_index = n%self.number_line_colors_defined+1
3154 if j>0 or merging[j]!='none':
3155 plot_lines.append(
3156 "'%s' index %d using (($1+$2)/2):%d ls %d title ''"\
3157 %(HwU_name,block_ratio_pos,merging_var+3,color_index))
3158 uncertainty_plot_lines[-1]['merging_scale'] = get_uncertainty_lines(
3159 HwU_name, block_ratio_pos, merging_var+4, color_index+30,'',
3160 band='merging_scale' in use_band)
3161 if not alpsfact_var_pos is None:
3162 for j,alpsfact_var in enumerate(alpsfact_var_pos):
3163 uncertainty_plot_lines.append({})
3164 if j==0:
3165 color_index = k%self.number_line_colors_defined+1
3166 else:
3167 n=n+1
3168 color_index = n%self.number_line_colors_defined+1
3169 if j>0 or alpsfact[j]!='none':
3170 plot_lines.append(
3171 "'%s' index %d using (($1+$2)/2):%d ls %d title ''"\
3172 %(HwU_name,block_ratio_pos,alpsfact_var+3,color_index))
3173 uncertainty_plot_lines[-1]['alpsfact'] = get_uncertainty_lines(
3174 HwU_name, block_ratio_pos, alpsfact_var+4, color_index+40,'',
3175 band='alpsfact' in use_band)
3176
3177
3178
3179 for one_plot in uncertainty_plot_lines:
3180 for uncertainty_type, lines in one_plot.items():
3181 if not uncertainty_type in use_band:
3182 plot_lines.extend(lines)
3183
3184 for one_plot in uncertainty_plot_lines:
3185 for uncertainty_type, lines in one_plot.items():
3186 if uncertainty_type in use_band:
3187 plot_lines.extend(lines)
3188
3189 plot_lines.append("1.0 ls 999 title ''")
3190
3191
3192 plot_lines.reverse()
3193
3194 gnuplot_out.append(',\\\n'.join(plot_lines))
3195
3196
3197 gnuplot_out.extend(['','unset label','',
3198 '################################################################################'])
3199
3200
3201 return block_position+len(self)
3202
3203 if __name__ == "__main__":
3204 main_doc = \
3205 """ For testing and standalone use. Usage:
3206 python histograms.py <.HwU input_file_path_1> <.HwU input_file_path_2> ... --out=<output_file_path.format> <options>
3207 Where <options> can be a list of the following:
3208 '--help' See this message.
3209 '--gnuplot' or '' output the histograms read to gnuplot
3210 '--HwU' to output the histograms read to the raw HwU source.
3211 '--types=<type1>,<type2>,...' to keep only the type<i> when importing histograms.
3212 '--titles=<title1>,<title2>,...' to keep only the titles which have any of 'title<i>' in them (not necessarily equal to them)
3213 '--n_ratios=<integer>' Specifies how many curves must be considerd for the ratios.
3214 '--no_open' Turn off the automatic processing of the gnuplot output.
3215 '--show_full' to show the complete output of what was read.
3216 '--show_short' to show a summary of what was read.
3217 '--simple_ratios' to turn off correlations and error propagation in the ratio.
3218 '--sum' To sum all identical histograms together
3219 '--average' To average over all identical histograms
3220 '--rebin=<n>' Rebin the plots by merging n-consecutive bins together.
3221 '--assign_types=<type1>,<type2>,...' to assign a type to all histograms of the first, second, etc... files loaded.
3222 '--multiply=<fact1>,<fact2>,...' to multiply all histograms of the first, second, etc... files by the fact1, fact2, etc...
3223 '--no_suffix' Do no add any suffix (like '#1, #2, etc..) to the histograms types.
3224 '--lhapdf-config=<PATH_TO_LHAPDF-CONFIG>' give path to lhapdf-config to compute PDF certainties using LHAPDF (only for lhapdf6)
3225 '--jet_samples=[int1,int2]' Specifies what jet samples to keep. 'None' is the default and keeps them all.
3226 '--central_only' This option specifies to disregard all extra weights, so as to make it possible
3227 to take the ratio of plots with different extra weights specified.
3228 '--keep_all_weights' This option specifies to keep in the HwU produced all the weights, even
3229 those which are not known (i.e. that is scale, PDF or merging variation)
3230 For chosing what kind of variation you want to see on your plot, you can use the following options
3231 '--no_<type>' Turn off the plotting of variations of the chosen type
3232 '--only_<type>' Turn on only the plotting of variations of the chosen type
3233 '--variations=['<type1>',...]' Turn on only the plotting of the variations of the list of chosen types
3234 '--band=['<type1>',...]' Chose for which variations one should use uncertainty bands as opposed to lines
3235 The types can be: pdf, scale, stat, merging or alpsfact
3236 For the last two options one can use ...=all to automatically select all types.
3237
3238 When parsing an XML-formatted plot source output by the Pythia8 driver, the file names can be appended
3239 options as suffixes separated by '|', as follows:
3240 python histograms.py <XML_source_file_name>@<option1>@<option2>@etc..
3241 These options can be
3242 'run_id=<integer>' Specifies the run_ID from which the plots should be loaded.
3243 By default, the first run is considered and the ones that follow are ignored.
3244 'merging_scale=<float>' This option allows to specify to import only the plots corresponding to a specific
3245 value for the merging scale.
3246 A value of -1 means that only the weights with the same merging scale as the central weight are kept.
3247 By default, all weights are considered.
3248 """
3249
3250 possible_options=['--help', '--gnuplot', '--HwU', '--types','--n_ratios',\
3251 '--no_open','--show_full','--show_short','--simple_ratios','--sum','--average','--rebin', \
3252 '--assign_types','--multiply','--no_suffix', '--out', '--jet_samples',
3253 '--no_scale','--no_pdf','--no_stat','--no_merging','--no_alpsfact',
3254 '--only_scale','--only_pdf','--only_stat','--only_merging','--only_alpsfact',
3255 '--variations','--band','--central_only', '--lhapdf-config','--titles',
3256 '--keep_all_weights']
3257 n_ratios = -1
3258 uncertainties = ['scale','pdf','statistical','merging_scale','alpsfact']
3259
3260 use_band = None
3261 auto_open = True
3262 ratio_correlations = True
3263 consider_reweights = ['pdf','scale','murmuf_scales','merging_scale','alpsfact']
3264
3265 - def log(msg):
3266 print "histograms.py :: %s"%str(msg)
3267
3268 if '--help' in sys.argv or len(sys.argv)==1:
3269 log('\n\n%s'%main_doc)
3270 sys.exit(0)
3271
3272 for arg in sys.argv[1:]:
3273 if arg.startswith('--'):
3274 if arg.split('=')[0] not in possible_options:
3275 log('WARNING: option "%s" not valid. It will be ignored' % arg)
3276
3277 arg_string=' '.join(sys.argv)
3278
3279 OutName = ""
3280 for arg in sys.argv[1:]:
3281 if arg.startswith('--out='):
3282 OutName = arg[6:]
3283
3284 accepted_types = []
3285 for arg in sys.argv[1:]:
3286 if arg.startswith('--types='):
3287 accepted_types = [(type if type!='None' else None) for type in \
3288 arg[8:].split(',')]
3289
3290 accepted_titles = []
3291 for arg in sys.argv[1:]:
3292 if arg.startswith('--titles='):
3293 accepted_titles = [(type if type!='None' else None) for type in \
3294 arg[9:].split(',')]
3295
3296 assigned_types = []
3297 for arg in sys.argv[1:]:
3298 if arg.startswith('--assign_types='):
3299 assigned_types = [(type if type!='None' else None) for type in \
3300 arg[15:].split(',')]
3301
3302 jet_samples_to_keep = None
3303
3304 lhapdfconfig = ['lhapdf-config']
3305 for arg in sys.argv[1:]:
3306 if arg.startswith('--lhapdf-config='):
3307 lhapdfconfig = arg[16:]
3308
3309 no_suffix = False
3310 if '--no_suffix' in sys.argv:
3311 no_suffix = True
3312
3313 if '--central_only' in sys.argv:
3314 consider_reweights = []
3315
3316 if '--keep_all_weights' in sys.argv:
3317 consider_reweights = 'ALL'
3318
3319 for arg in sys.argv[1:]:
3320 if arg.startswith('--n_ratios='):
3321 n_ratios = int(arg[11:])
3322
3323 if '--no_open' in sys.argv:
3324 auto_open = False
3325
3326 variation_type_map={'scale':'scale','merging':'merging_scale','pdf':'pdf',
3327 'stat':'statistical','alpsfact':'alpsfact'}
3328
3329 for arg in sys.argv:
3330 try:
3331 opt, value = arg.split('=')
3332 except ValueError:
3333 continue
3334 if opt=='--jet_samples':
3335 jet_samples_to_keep = eval(value)
3336 if opt=='--variations':
3337 uncertainties=[variation_type_map[type] for type in eval(value,
3338 dict([(key,key) for key in variation_type_map.keys()]+
3339 [('all',variation_type_map.keys())]))]
3340 if opt=='--band':
3341 use_band=[variation_type_map[type] for type in eval(value,
3342 dict([(key,key) for key in variation_type_map.keys()]+
3343 [('all',[type for type in variation_type_map.keys() if type!='stat'])]))]
3344
3345 if '--simple_ratios' in sys.argv:
3346 ratio_correlations = False
3347
3348 for arg in sys.argv:
3349 if arg.startswith('--no_') and not arg.startswith('--no_open'):
3350 uncertainties.remove(variation_type_map[arg[5:]])
3351 if arg.startswith('--only_'):
3352 uncertainties= [variation_type_map[arg[7:]]]
3353 break
3354
3355
3356
3357 if isinstance(consider_reweights, list):
3358 naming_map={'pdf':'pdf','scale':'scale',
3359 'merging_scale':'merging_scale','alpsfact':'alpsfact'}
3360 for key in naming_map:
3361 if (not key in uncertainties) and (naming_map[key] in consider_reweights):
3362 consider_reweights.remove(naming_map[key])
3363
3364 n_files = len([_ for _ in sys.argv[1:] if not _.startswith('--')])
3365 histo_norm = [1.0]*n_files
3366
3367 for arg in sys.argv[1:]:
3368 if arg.startswith('--multiply='):
3369 histo_norm = [(float(fact) if fact!='' else 1.0) for fact in \
3370 arg[11:].split(',')]
3371
3372 if '--average' in sys.argv:
3373 histo_norm = [hist/float(n_files) for hist in histo_norm]
3374
3375 log("=======")
3376 histo_list = HwUList([])
3377 for i, arg in enumerate(sys.argv[1:]):
3378 if arg.startswith('--'):
3379 break
3380 log("Loading histograms from '%s'."%arg)
3381 if OutName=="":
3382 OutName = os.path.basename(arg).split('.')[0]+'_output'
3383
3384 file_specification = arg.split('@')
3385 filename = file_specification.pop(0)
3386 file_options = {}
3387 for option in file_specification:
3388 opt, value = option.split('=')
3389 if opt=='run_id':
3390 file_options[opt]=int(value)
3391 if opt=='merging_scale':
3392 file_options[opt]=float(value)
3393 else:
3394 log("Unreckognize file option '%s'."%option)
3395 sys.exit(1)
3396 new_histo_list = HwUList(filename, accepted_types_order=accepted_types,
3397 consider_reweights=consider_reweights, **file_options)
3398
3399 if len(accepted_titles)>0:
3400 new_histo_list = HwUList(histo for histo in new_histo_list if
3401 any(t in histo.title for t in accepted_titles))
3402 for histo in new_histo_list:
3403 if no_suffix or n_files==1:
3404 continue
3405 if not histo.type is None:
3406 histo.type += '|'
3407 else:
3408 histo.type = ''
3409
3410
3411
3412
3413
3414
3415 try:
3416 suffix = assigned_types[i]
3417 except IndexError:
3418 suffix = "#%d"%(i+1)
3419 try:
3420 histo.type = histo.type[:histo.type.index('#')] + suffix
3421 except ValueError:
3422 histo.type += suffix
3423
3424 if i==0 or all(_ not in ['--sum','--average'] for _ in sys.argv):
3425 for j,hist in enumerate(new_histo_list):
3426 new_histo_list[j]=hist*histo_norm[i]
3427 histo_list.extend(new_histo_list)
3428 continue
3429
3430 if any(_ in sys.argv for _ in ['--sum','--average']):
3431 for j, hist in enumerate(new_histo_list):
3432
3433 hist.test_plot_compability(histo_list[j])
3434
3435 histo_list[j] += hist*histo_norm[i]
3436
3437 log("A total of %i histograms were found."%len(histo_list))
3438 log("=======")
3439
3440 n_rebin = 1
3441 for arg in sys.argv[1:]:
3442 if arg.startswith('--rebin='):
3443 n_rebin = int(arg[8:])
3444
3445 if n_rebin > 1:
3446 for hist in histo_list:
3447 hist.rebin(n_rebin)
3448
3449 if '--gnuplot' in sys.argv or all(arg not in ['--HwU'] for arg in sys.argv):
3450
3451 histo_list.output(OutName, format='gnuplot',
3452 number_of_ratios = n_ratios,
3453 uncertainties=uncertainties,
3454 ratio_correlations=ratio_correlations,
3455 arg_string=arg_string,
3456 jet_samples_to_keep=jet_samples_to_keep,
3457 use_band=use_band,
3458 auto_open=auto_open,
3459 lhapdfconfig=lhapdfconfig)
3460
3461 log("%d histograms have been output in " % len(histo_list)+\
3462 "the gnuplot format at '%s.[HwU|gnuplot]'." % OutName)
3463 if auto_open:
3464 command = 'gnuplot %s.gnuplot'%OutName
3465 try:
3466 subprocess.call(command,shell=True,stderr=subprocess.PIPE)
3467 except:
3468 log("Automatic processing of the gnuplot card failed. Try the"+\
3469 " command by hand:\n%s"%command)
3470 else:
3471 sys.exit(0)
3472
3473 if '--HwU' in sys.argv:
3474 log("Histograms data has been output in the HwU format at "+\
3475 "'%s.HwU'."%OutName)
3476 histo_list.output(OutName, format='HwU')
3477 sys.exit(0)
3478
3479 if '--show_short' in sys.argv or '--show_full' in sys.argv:
3480 for i, histo in enumerate(histo_list):
3481 if i!=0:
3482 log('-------')
3483 log(histo.nice_string(short=(not '--show_full' in sys.argv)))
3484 log("=======")
3489 ''' Fills a hole in matplotlib: fill_between for step plots.
3490 Parameters :
3491 ------------
3492 x : array-like
3493 Array/vector of index values. These are assumed to be equally-spaced.
3494 If not, the result will probably look weird...
3495 y1 : array-like
3496 Array/vector of values to be filled under.
3497 y2 : array-Like
3498 Array/vector or bottom values for filled area. Default is 0.
3499 **kwargs will be passed to the matplotlib fill_between() function.
3500 '''
3501
3502 if ax is None:
3503 ax = plt.gca()
3504
3505
3506
3507
3508 xx= []; [(xx.append(d),xx.append(d)) for d in x]; xx = xx[1:]
3509
3510 xstep = x[1] -x[0]
3511
3512 xx.append(xx[-1] + xstep)
3513
3514
3515 if h_align == 'mid':
3516 xx = [X-xstep/2. for X in xx]
3517 elif h_align == 'right':
3518 xx = [X-xstep for X in xx]
3519
3520
3521 yy1 = []; [(yy1.append(d),yy1.append(d)) for d in y1]
3522 if isinstance(y1, list):
3523 yy2 = []; [(yy2.append(d),yy2.append(d)) for d in y2]
3524 else:
3525 yy2=y2
3526
3527
3528 ax.fill_between(xx, yy1, y2=yy2, **kwargs)
3529
3530 return ax
3531
3532