1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 from __future__ import division
16 import os
17 import math
18 import logging
19 import re
20 import xml.dom.minidom as minidom
21
22 logger = logging.getLogger('madevent.stdout')
23
24 pjoin = os.path.join
25 try:
26 import madgraph
27 except ImportError:
28 import internal.cluster as cluster
29 import internal.misc as misc
30 from internal import MadGraph5Error
31 else:
32 import madgraph.various.cluster as cluster
33 import madgraph.various.misc as misc
34 from madgraph import MadGraph5Error
35
37 """ A class to store statistics about a MadEvent run. """
38
40 """ Initialize the run dictionary. For now, the same as a regular
41 dictionary, except that we specify some default statistics. """
42
43 madloop_statistics = {
44 'unknown_stability' : 0,
45 'stable_points' : 0,
46 'unstable_points' : 0,
47 'exceptional_points' : 0,
48 'DP_usage' : 0,
49 'QP_usage' : 0,
50 'DP_init_usage' : 0,
51 'QP_init_usage' : 0,
52 'CutTools_DP_usage' : 0,
53 'CutTools_QP_usage' : 0,
54 'PJFry_usage' : 0,
55 'Golem_usage' : 0,
56 'IREGI_usage' : 0,
57 'max_precision' : 1.0e99,
58 'min_precision' : 0.0,
59 'averaged_timing' : 0.0,
60 'n_madloop_calls' : 0,
61 'cumulative_timing' : 0.0,
62 'skipped_subchannel' : 0
63
64 }
65
66 for key, value in madloop_statistics.items():
67 self[key] = value
68
69 super(dict,self).__init__(*args, **opts)
70
72 """ Update the current statitistics with the new_stats specified."""
73
74 if isinstance(new_stats,RunStatistics):
75 new_stats = [new_stats, ]
76 elif isinstance(new_stats,list):
77 if any(not isinstance(_,RunStatistics) for _ in new_stats):
78 raise MadGraph5Error, "The 'new_stats' argument of the function "+\
79 "'updtate_statistics' must be a (possibly list of) "+\
80 "RunStatistics instance."
81
82 keys = set([])
83 for stat in [self,]+new_stats:
84 keys |= set(stat.keys())
85
86 new_stats = new_stats+[self,]
87 for key in keys:
88
89 if key=='max_precision':
90
91 self[key] = min( _[key] for _ in new_stats if key in _)
92 elif key=='min_precision':
93
94 self[key] = max( _[key] for _ in new_stats if key in _)
95 elif key=='averaged_timing':
96 n_madloop_calls = sum(_['n_madloop_calls'] for _ in new_stats if
97 'n_madloop_calls' in _)
98 if n_madloop_calls > 0 :
99 self[key] = sum(_[key]*_['n_madloop_calls'] for _ in
100 new_stats if (key in _ and 'n_madloop_calls' in _) )/n_madloop_calls
101 else:
102
103 self[key] = sum(_[key] for _ in new_stats if key in _)
104
106 """ Load the statistics from an xml node. """
107
108 def getData(Node):
109 return Node.childNodes[0].data
110
111 u_return_code = xml_node.getElementsByTagName('u_return_code')
112 u_codes = [int(_) for _ in getData(u_return_code[0]).split(',')]
113 self['CutTools_DP_usage'] = u_codes[1]
114 self['PJFry_usage'] = u_codes[2]
115 self['IREGI_usage'] = u_codes[3]
116 self['Golem_usage'] = u_codes[4]
117 self['CutTools_QP_usage'] = u_codes[9]
118 t_return_code = xml_node.getElementsByTagName('t_return_code')
119 t_codes = [int(_) for _ in getData(t_return_code[0]).split(',')]
120 self['DP_usage'] = t_codes[1]
121 self['QP_usage'] = t_codes[2]
122 self['DP_init_usage'] = t_codes[3]
123 self['DP_init_usage'] = t_codes[4]
124 h_return_code = xml_node.getElementsByTagName('h_return_code')
125 h_codes = [int(_) for _ in getData(h_return_code[0]).split(',')]
126 self['unknown_stability'] = h_codes[1]
127 self['stable_points'] = h_codes[2]
128 self['unstable_points'] = h_codes[3]
129 self['exceptional_points'] = h_codes[4]
130 average_time = xml_node.getElementsByTagName('average_time')
131 avg_time = float(getData(average_time[0]))
132 self['averaged_timing'] = avg_time
133 cumulated_time = xml_node.getElementsByTagName('cumulated_time')
134 cumul_time = float(getData(cumulated_time[0]))
135 self['cumulative_timing'] = cumul_time
136 max_prec = xml_node.getElementsByTagName('max_prec')
137 max_prec = float(getData(max_prec[0]))
138
139 self['min_precision'] = max_prec
140 min_prec = xml_node.getElementsByTagName('min_prec')
141 min_prec = float(getData(min_prec[0]))
142
143 self['max_precision'] = min_prec
144 n_evals = xml_node.getElementsByTagName('n_evals')
145 n_evals = int(getData(n_evals[0]))
146 self['n_madloop_calls'] = n_evals
147
149 """Returns a one-line string summarizing the run statistics
150 gathered for the channel G."""
151
152
153
154 if self['n_madloop_calls']==0:
155 return ''
156
157 stability = [
158 ('tot#',self['n_madloop_calls']),
159 ('unkwn#',self['unknown_stability']),
160 ('UPS%',float(self['unstable_points'])/self['n_madloop_calls']),
161 ('EPS#',self['exceptional_points'])]
162
163 stability = [_ for _ in stability if _[1] > 0 or _[0] in ['UPS%','EPS#']]
164 stability = [(_[0],'%i'%_[1]) if isinstance(_[1], int) else
165 (_[0],'%.3g'%(100.0*_[1])) for _ in stability]
166
167 tools_used = [
168 ('CT_DP',float(self['CutTools_DP_usage'])/self['n_madloop_calls']),
169 ('CT_QP',float(self['CutTools_QP_usage'])/self['n_madloop_calls']),
170 ('PJFry',float(self['PJFry_usage'])/self['n_madloop_calls']),
171 ('Golem',float(self['Golem_usage'])/self['n_madloop_calls']),
172 ('IREGI',float(self['IREGI_usage'])/self['n_madloop_calls'])]
173
174 tools_used = [(_[0],'%.3g'%(100.0*_[1])) for _ in tools_used if _[1] > 0.0 ]
175
176 to_print = [('%s statistics:'%(G if isinstance(G,str) else
177 str(os.path.join(list(G))))\
178 +(' %s,'%misc.format_time(int(self['cumulative_timing'])) if
179 int(self['cumulative_timing']) > 0 else '')
180 +((' Avg. ML timing = %i ms'%int(1.0e3*self['averaged_timing'])) if
181 self['averaged_timing'] > 0.001 else
182 (' Avg. ML timing = %i mus'%int(1.0e6*self['averaged_timing']))) \
183 +', Min precision = %.2e'%self['min_precision'])
184 ,' -> Stability %s'%dict(stability)
185 ,' -> Red. tools usage in %% %s'%dict(tools_used)
186
187
188
189
190
191 ]
192
193 if self['skipped_subchannel'] > 0 and not no_warning:
194 to_print.append("WARNING: Some event with large weight have been "+\
195 "discarded. This happened %s times." % self['skipped_subchannel'])
196
197 return ('\n'.join(to_print)).replace("'"," ")
198
200 """return if any stat needs to be reported as a warning
201 When this is True, the print_warning doit retourner un warning
202 """
203
204 if self['n_madloop_calls'] > 0:
205 fraction = self['exceptional_points']/float(self['n_madloop_calls'])
206 else:
207 fraction = 0.0
208
209 if self['skipped_subchannel'] > 0:
210 return True
211 elif fraction > 1.0e-4:
212 return True
213 else:
214 return False
215
217 """get a string with all the identified warning"""
218
219 to_print = []
220 if self['skipped_subchannel'] > 0:
221 to_print.append("Some event with large weight have been discarded."+\
222 " This happens %s times." % self['skipped_subchannel'])
223 if self['n_madloop_calls'] > 0:
224 fraction = self['exceptional_points']/float(self['n_madloop_calls'])
225 if fraction > 1.0e-4:
226 to_print.append("Some PS with numerical instability have been set "+\
227 "to a zero matrix-element (%.3g%%)" % (100.0*fraction))
228
229 return ('\n'.join(to_print)).replace("'"," ")
230
232
234 """Initialize all data """
235
236 self.run_statistics = RunStatistics()
237 self.name = name
238 self.parent_name = ''
239 self.axsec = 0
240 self.xsec = 0
241 self.xerru = 0
242 self.xerrc = 0
243 self.nevents = 0
244 self.nw = 0
245 self.maxit = 0
246 self.nunwgt = 0
247 self.luminosity = 0
248 self.mfactor = 1
249 self.ysec_iter = []
250 self.yerr_iter = []
251 self.yasec_iter = []
252 self.eff_iter = []
253 self.maxwgt_iter = []
254 self.maxwgt = 0
255 self.th_maxwgt= 0
256
257 self.th_nunwgt = 0
258
259
260 return
261
262
264 """read results.dat and fullfill information"""
265
266 if isinstance(filepath, str):
267 finput = open(filepath)
268 elif isinstance(filepath, file):
269 finput = filepath
270 else:
271 raise Exception, "filepath should be a path or a file descriptor"
272
273 i=0
274 found_xsec_line = False
275 for line in finput:
276
277
278 if '<' in line:
279 break
280 i+=1
281 if i == 1:
282 def secure_float(d):
283 try:
284 return float(d)
285 except ValueError:
286 m=re.search(r'''([+-]?[\d.]*)([+-]\d*)''', d)
287 if m:
288 return float(m.group(1))*10**(float(m.group(2)))
289 return
290
291 data = [secure_float(d) for d in line.split()]
292 self.axsec, self.xerru, self.xerrc, self.nevents, self.nw,\
293 self.maxit, self.nunwgt, self.luminosity, self.wgt, \
294 self.xsec = data[:10]
295 if len(data) > 10:
296 self.maxwgt = data[10]
297 if len(data) >12:
298 self.th_maxwgt, self.th_nunwgt = data[11:13]
299 if self.mfactor > 1:
300 self.luminosity /= self.mfactor
301 continue
302 try:
303 l, sec, err, eff, maxwgt, asec = line.split()
304 found_xsec_line = True
305 except:
306 break
307 self.ysec_iter.append(secure_float(sec))
308 self.yerr_iter.append(secure_float(err))
309 self.yasec_iter.append(secure_float(asec))
310 self.eff_iter.append(secure_float(eff))
311 self.maxwgt_iter.append(secure_float(maxwgt))
312
313 finput.seek(0)
314 xml = []
315 for line in finput:
316 if re.match('^.*<.*>',line):
317 xml.append(line)
318 break
319 for line in finput:
320 xml.append(line)
321
322 if xml:
323 self.parse_xml_results('\n'.join(xml))
324
325
326 if self.nevents == 0 and self.nunwgt == 0 and isinstance(filepath, str) and \
327 os.path.exists(pjoin(os.path.split(filepath)[0], 'nevts')):
328 nevts = int(open(pjoin(os.path.split(filepath)[0], 'nevts')).read())
329 self.nevents = nevts
330 self.nunwgt = nevts
331
333 """ Parse the xml part of the results.dat file."""
334
335 dom = minidom.parseString(xml)
336
337 statistics_node = dom.getElementsByTagName("run_statistics")
338
339 if statistics_node:
340 try:
341 self.run_statistics.load_statistics(statistics_node[0])
342 except ValueError, IndexError:
343 logger.warning('Fail to read run statistics from results.dat')
344
346 self.mfactor = int(value)
347
349 """Change the number of iterations for this process"""
350
351 if len(self.ysec_iter) <= nb_iter:
352 return
353
354
355 nb_to_rm = len(self.ysec_iter) - nb_iter
356 ysec = [0]
357 yerr = [0]
358 for i in range(nb_to_rm):
359 ysec[0] += self.ysec_iter[i]
360 yerr[0] += self.yerr_iter[i]**2
361 ysec[0] /= (nb_to_rm+1)
362 yerr[0] = math.sqrt(yerr[0]) / (nb_to_rm + 1)
363
364 for i in range(1, nb_iter):
365 ysec[i] = self.ysec_iter[nb_to_rm + i]
366 yerr[i] = self.yerr_iter[nb_to_rm + i]
367
368 self.ysec_iter = ysec
369 self.yerr_iter = yerr
370
371 - def get(self, name):
372
373 if name in ['xsec', 'xerru','xerrc']:
374 return getattr(self, name) * self.mfactor
375 elif name in ['luminosity']:
376
377
378 return getattr(self, name)
379 elif (name == 'eff'):
380 return self.xerr*math.sqrt(self.nevents/(self.xsec+1e-99))
381 elif name == 'xerr':
382 return math.sqrt(self.xerru**2+self.xerrc**2)
383 elif name == 'name':
384 return pjoin(self.parent_name, self.name)
385 else:
386 return getattr(self, name)
387
389
394
396 """read the data in the file"""
397 try:
398 oneresult = OneResult(name)
399 oneresult.set_mfactor(mfactor)
400 oneresult.read_results(filepath)
401 oneresult.parent_name = self.name
402 self.append(oneresult)
403 return oneresult
404 except Exception:
405 logger.critical("Error when reading %s" % filepath)
406 raise
407
408
410 """compute the value associate to this combination"""
411
412 self.compute_iterations()
413 self.axsec = sum([one.axsec for one in self])
414 self.xsec = sum([one.xsec for one in self])
415 self.xerrc = sum([one.xerrc for one in self])
416 self.xerru = math.sqrt(sum([one.xerru**2 for one in self]))
417
418 self.nevents = sum([one.nevents for one in self])
419 self.nw = sum([one.nw for one in self])
420 self.maxit = len(self.yerr_iter)
421 self.nunwgt = sum([one.nunwgt for one in self])
422 self.wgt = 0
423 self.luminosity = min([0]+[one.luminosity for one in self])
424 if update_statistics:
425 self.run_statistics.aggregate_statistics([_.run_statistics for _ in self])
426
428 """compute the value associate to this combination"""
429
430 nbjobs = len(self)
431 if not nbjobs:
432 return
433 self.axsec = sum([one.axsec for one in self]) / nbjobs
434 self.xsec = sum([one.xsec for one in self]) /nbjobs
435 self.xerrc = sum([one.xerrc for one in self]) /nbjobs
436 self.xerru = math.sqrt(sum([one.xerru**2 for one in self])) /nbjobs
437
438 self.nevents = sum([one.nevents for one in self])
439 self.nw = 0
440 self.maxit = 0
441 self.nunwgt = sum([one.nunwgt for one in self])
442 self.wgt = 0
443 self.luminosity = sum([one.luminosity for one in self])
444 self.ysec_iter = []
445 self.yerr_iter = []
446 self.th_maxwgt = 0.0
447 self.th_nunwgt = 0
448 for result in self:
449 self.ysec_iter+=result.ysec_iter
450 self.yerr_iter+=result.yerr_iter
451 self.yasec_iter += result.yasec_iter
452 self.eff_iter += result.eff_iter
453 self.maxwgt_iter += result.maxwgt_iter
454
455
456
458 """Compute iterations to have a chi-square on the stability of the
459 integral"""
460
461 nb_iter = min([len(a.ysec_iter) for a in self], 0)
462
463 for oneresult in self:
464 oneresult.change_iterations_number(nb_iter)
465
466
467 for i in range(nb_iter):
468 value = [one.ysec_iter[i] for one in self]
469 error = [one.yerr_iter[i]**2 for one in self]
470
471
472 self.ysec_iter.append(sum(value))
473 self.yerr_iter.append(math.sqrt(sum(error)))
474
475
476 template_file = \
477 """
478 %(diagram_link)s
479 <BR>
480 <b>s= %(cross).5g ± %(error).3g (%(unit)s)</b><br><br>
481 <table class="sortable" id='tablesort'>
482 <tr><th>Graph</th>
483 <th> %(result_type)s</th>
484 <th>Error</th>
485 <th>Events (K)</th>
486 <th>Unwgt</th>
487 <th>Luminosity</th>
488 </tr>
489 %(table_lines)s
490 </table>
491 </center>
492 <br><br><br>
493 """
494 table_line_template = \
495 """
496 <tr><td align=right>%(P_title)s</td>
497 <td align=right><a id="%(P_link)s" href=%(P_link)s onClick="check_link('%(P_link)s','%(mod_P_link)s','%(P_link)s')"> %(cross)s </a> </td>
498 <td align=right> %(error)s</td>
499 <td align=right> %(events)s</td>
500 <td align=right> %(unweighted)s</td>
501 <td align=right> %(luminosity)s</td>
502 </tr>
503 """
504
505 - def get_html(self,run, unit, me_dir = []):
506 """write html output"""
507
508
509 P_grouping = {}
510
511 tables_line = ''
512 for oneresult in self:
513 if oneresult.name.startswith('P'):
514 title = '<a href=../../SubProcesses/%(P)s/diagrams.html>%(P)s</a>' \
515 % {'P':oneresult.name}
516 P = oneresult.name.split('_',1)[0]
517 if P in P_grouping:
518 P_grouping[P] += float(oneresult.xsec)
519 else:
520 P_grouping[P] = float(oneresult.xsec)
521 else:
522 title = oneresult.name
523
524 if not isinstance(oneresult, Combine_results):
525
526 if os.path.exists(pjoin(me_dir, 'Events', run, 'alllogs_1.html')):
527 link = '../../Events/%(R)s/alllogs_1.html#/%(P)s/%(G)s' % \
528 {'P': self.name,
529 'G': oneresult.name,
530 'R': run}
531 mod_link = link
532 elif os.path.exists(pjoin(me_dir, 'Events', run, 'alllogs_0.html')):
533 link = '../../Events/%(R)s/alllogs_0.html#/%(P)s/%(G)s' % \
534 {'P': self.name,
535 'G': oneresult.name,
536 'R': run}
537 mod_link = link
538 else:
539
540 link = '../../SubProcesses/%(P)s/%(G)s/%(R)s_log.txt' % \
541 {'P': self.name,
542 'G': oneresult.name,
543 'R': run}
544 mod_link = '../../SubProcesses/%(P)s/%(G)s/log.txt' % \
545 {'P': self.name,
546 'G': oneresult.name}
547 else:
548 link = '#%s' % oneresult.name
549 mod_link = link
550
551 dico = {'P_title': title,
552 'P_link': link,
553 'mod_P_link': mod_link,
554 'cross': '%.4g' % oneresult.xsec,
555 'error': '%.3g' % oneresult.xerru,
556 'events': oneresult.nevents/1000.0,
557 'unweighted': oneresult.nunwgt,
558 'luminosity': '%.3g' % oneresult.luminosity
559 }
560
561 tables_line += self.table_line_template % dico
562
563 for P_name, cross in P_grouping.items():
564 dico = {'P_title': '%s sum' % P_name,
565 'P_link': './results.html',
566 'mod_P_link':'',
567 'cross': cross,
568 'error': '',
569 'events': '',
570 'unweighted': '',
571 'luminosity': ''
572 }
573 tables_line += self.table_line_template % dico
574
575 if self.name.startswith('P'):
576 title = '<dt><a name=%(P)s href=../../SubProcesses/%(P)s/diagrams.html>%(P)s</a></dt><dd>' \
577 % {'P':self.name}
578 else:
579 title = ''
580
581 dico = {'cross': self.xsec,
582 'abscross': self.axsec,
583 'error': self.xerru,
584 'unit': unit,
585 'result_type': 'Cross-Section',
586 'table_lines': tables_line,
587 'diagram_link': title
588 }
589
590 html_text = self.template_file % dico
591 return html_text
592
594 """write a correctly formatted results.dat"""
595
596 def fstr(nb):
597 data = '%E' % nb
598 if data == 'NAN':
599 nb, power = 0,0
600 else:
601 nb, power = data.split('E')
602 nb = float(nb) /10
603 power = int(power) + 1
604 return '%.5fE%+03i' %(nb,power)
605
606 line = '%s %s %s %i %i %i %i %s %s %s %s %s %i\n' % (fstr(self.axsec), fstr(self.xerru),
607 fstr(self.xerrc), self.nevents, self.nw, self.maxit, self.nunwgt,
608 fstr(self.luminosity), fstr(self.wgt), fstr(self.xsec), fstr(self.maxwgt),
609 fstr(self.th_maxwgt), self.th_nunwgt)
610 fsock = open(output_path,'w')
611 fsock.writelines(line)
612 for i in range(len(self.ysec_iter)):
613 line = '%s %s %s %s %s %s\n' % (i+1, self.ysec_iter[i], self.yerr_iter[i],
614 self.eff_iter[i], self.maxwgt_iter[i], self.yasec_iter[i])
615 fsock.writelines(line)
616
617
618
619 results_header = """
620 <head>
621 <title>Process results</title>
622 <script type="text/javascript" src="../sortable.js"></script>
623 <link rel=stylesheet href="../mgstyle.css" type="text/css">
624 </head>
625 <body>
626 <script type="text/javascript">
627 function UrlExists(url) {
628 var http = new XMLHttpRequest();
629 http.open('HEAD', url, false);
630 try{
631 http.send()
632 }
633 catch(err){
634 return 1==2;
635 }
636 return http.status!=404;
637 }
638 function check_link(url,alt, id){
639 var obj = document.getElementById(id);
640 if ( ! UrlExists(url)){
641 if ( ! UrlExists(alt)){
642 obj.href = alt;
643 return true;
644 }
645 obj.href = alt;
646 return false;
647 }
648 obj.href = url;
649 return 1==1;
650 }
651 </script>
652 """
653
655 """ """
656
657 run = cmd.results.current['run_name']
658 all = Combine_results(run)
659
660 for Pdir in open(pjoin(cmd.me_dir, 'SubProcesses','subproc.mg')):
661 Pdir = Pdir.strip()
662 P_comb = Combine_results(Pdir)
663
664 P_path = pjoin(cmd.me_dir, 'SubProcesses', Pdir)
665 G_dir = [G for G in os.listdir(P_path) if G.startswith('G') and
666 os.path.isdir(pjoin(P_path,G))]
667
668 try:
669 for line in open(pjoin(P_path, 'symfact.dat')):
670 name, mfactor = line.split()
671 if float(mfactor) < 0:
672 continue
673 if os.path.exists(pjoin(P_path, 'ajob.no_ps.log')):
674 continue
675
676 if not folder_names:
677 name = 'G' + name
678 P_comb.add_results(name, pjoin(P_path,name,'results.dat'), mfactor)
679 else:
680 for folder in folder_names:
681 if 'G' in folder:
682 dir = folder.replace('*', name)
683 else:
684 dir = folder.replace('*', '_G' + name)
685 P_comb.add_results(dir, pjoin(P_path,dir,'results.dat'), mfactor)
686 except IOError:
687 continue
688 P_comb.compute_values()
689 all.append(P_comb)
690 all.compute_values()
691 return all
692
693
695 """ folder_names has been added for the amcatnlo runs """
696 run = cmd.results.current['run_name']
697 if not os.path.exists(pjoin(cmd.me_dir, 'HTML', run)):
698 os.mkdir(pjoin(cmd.me_dir, 'HTML', run))
699
700 unit = cmd.results.unit
701 P_text = ""
702 Presults = collect_result(cmd, folder_names=folder_names)
703
704
705 for P_comb in Presults:
706 P_text += P_comb.get_html(run, unit, cmd.me_dir)
707 P_comb.compute_values()
708 P_comb.write_results_dat(pjoin(cmd.me_dir, 'SubProcesses', P_comb.name,
709 '%s_results.dat' % run))
710
711
712 Presults.write_results_dat(pjoin(cmd.me_dir,'SubProcesses', 'results.dat'))
713
714 fsock = open(pjoin(cmd.me_dir, 'HTML', run, 'results.html'),'w')
715 fsock.write(results_header)
716 fsock.write('%s <dl>' % Presults.get_html(run, unit, cmd.me_dir))
717 fsock.write('%s </dl></body>' % P_text)
718
719 return Presults.xsec, Presults.xerru
720