Belle II Software  release-05-01-25
latexReporting.py
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3 #
4 # Thomas Keck 2017
5 
6 """
7  Call this "python3 fei/latexReporting.py summary.tex"
8  in a directory containing the monitoring output of the FEI
9  It will create a latex document containing a summary and plots
10  and tries to compile this summary.tex into a PDF file summary.pdf
11 
12  You can improve / modify this script
13  E.g. If you want to add new plots:
14  Add your plot in the monitoring.py file
15  Add your plot below using b2latex.Graphics
16 """
17 
18 
19 import ROOT
20 from ROOT import Belle2
21 
22 from fei import monitoring
23 
24 from B2Tools import b2latex
25 from B2Tools import format
26 
27 import pickle
28 import sys
29 import glob
30 
31 
32 if __name__ == '__main__':
33  try:
34  output_file = sys.argv[1]
35  except IndexError:
36  raise AttributeError("You have to supply the output tex file.")
37 
38  particles, configuration = monitoring.load_config()
39  monitoringParticle = []
40  for particle in particles:
41  monitoringParticle.append(monitoring.MonitoringParticle(particle))
42 
43  # Create latex file
44  o = b2latex.LatexFile()
45 
46  o += b2latex.TitlePage(title='Full Event Interpretation Report',
47  authors=['Thomas Keck', 'Christian Pulvermacher', 'William Sutcliffe'],
48  abstract=r"""
49  This report contains key performance indicators and control plots of the Full Event Interpretation.
50  The pre-, and post-cuts as well as trained multivariate selection methods are described.
51  Furthermore the resulting purities and efficiencies are stated.
52  """,
53  add_table_of_contents=True).finish()
54 
55  o += b2latex.Section("Summary").finish()
56  o += b2latex.String(r"""
57  For each decay channel of each particle a multivariate selection method is trained after applying
58  a fast pre-cut on the candidates. Afterwards, a post-cut is applied on the signal probability calculated by the method.
59  This reduces combinatorics in the following stages of the Full
60  Event Interpretation.
61  """).finish()
62 
63  table = b2latex.LongTable(columnspecs=r'c|rr|rrrrrr',
64  caption='Per-particle efficiency before and after the applied pre- and post-cut.',
65  head=r'Particle & \multicolumn{2}{c}{Covered BR} '
66  r' & \multicolumn{3}{c}{pre-cut} & \multicolumn{3}{c}{post-cut} \\'
67  r' & exc & inc & user & ranking & vertex '
68  r' & absolute & ranking & unique',
69  format_string=r'{name} & {exc_br:.3f} & {inc_br:.3f} & {user_pre_cut:.3f} & {ranking_pre_cut:.3f}'
70  r' & {vertex_pre_cut:.3f} & {absolute_post_cut:.3f}'
71  r' & {ranking_post_cut:.3f} & {after_tag:.3f}')
72 
73  for p in monitoringParticle:
74  table.add(name=format.decayDescriptor(p.particle.identifier),
75  exc_br=sum(p.exc_br_per_channel.values()),
76  inc_br=sum(p.inc_br_per_channel.values()),
77  user_pre_cut=sum(p.before_ranking.values()).efficiency,
78  ranking_pre_cut=sum(p.after_ranking.values()).efficiency,
79  vertex_pre_cut=sum(p.after_vertex.values()).efficiency,
80  absolute_post_cut=p.before_ranking_postcut.efficiency,
81  ranking_post_cut=p.after_ranking_postcut.efficiency,
82  after_tag=p.after_tag.efficiency)
83  o += table.finish()
84 
85  table = b2latex.LongTable(columnspecs=r'c|rrrrrr',
86  caption='Per-particle purity before and after the applied pre- and post-cut.',
87  head=r'Particle '
88  r' & \multicolumn{3}{c}{pre-cut} & \multicolumn{3}{c}{post-cut} \\'
89  r' & user & ranking & vertex '
90  r' & absolute & ranking & unique',
91  format_string=r'{name} & {user_pre_cut:.3f} & {ranking_pre_cut:.3f}'
92  r' & {vertex_pre_cut:.3f} & {absolute_post_cut:.3f}'
93  r' & {ranking_post_cut:.3f} & {after_tag:.3f}')
94 
95  for p in monitoringParticle:
96  table.add(name=format.decayDescriptor(p.particle.identifier),
97  user_pre_cut=sum(p.before_ranking.values()).purity,
98  ranking_pre_cut=sum(p.after_ranking.values()).purity,
99  vertex_pre_cut=sum(p.after_vertex.values()).purity,
100  absolute_post_cut=p.before_ranking_postcut.purity,
101  ranking_post_cut=p.after_ranking_postcut.purity,
102  after_tag=p.after_tag.purity)
103  o += table.finish()
104 
105  # If you change the number of colors, than change below \ifnum5 accordingly
106  moduleTypes = ['ParticleCombiner', 'MVAExpert', 'MCMatch', 'ParticleVertexFitter', 'BestCandidateSelection', 'Other']
107 
108  o += b2latex.Section("CPU time").finish()
109  colour_list = b2latex.DefineColourList()
110  o += colour_list.finish()
111 
112  for p in monitoringParticle:
113  o += b2latex.SubSection(format.decayDescriptor(p.particle.identifier)).finish()
114 
115  table = b2latex.LongTable(columnspecs=r'lrcrr',
116  caption='Total CPU time spent in event() calls for each channel. Bars show ' +
117  ', '.join('\\textcolor{%s}{%s}' % (c, m)
118  for c, m in zip(colour_list.colours, moduleTypes)) +
119  ', in this order. Does not include I/O, initialisation, training, post-cuts etc.',
120  head=r'Decay & CPU time & by module & per (true) candidate & Relative time ',
121  format_string=r'{name} & {time} & {bargraph} & {timePerCandidate} & {timePercent:.2f}\% ')
122 
123  tt_channel = sum(p.module_statistic.channel_time.values())
124  tt_particle = p.module_statistic.particle_time + sum(p.module_statistic.channel_time.values())
125  fraction = tt_channel / tt_particle * 100 if tt_particle > 0 else 0.0
126 
127  for channel in p.particle.channels:
128  time = p.time_per_channel[channel.label]
129  trueCandidates = p.after_classifier[channel.label].nSig
130  allCandidates = p.after_classifier[channel.label].nTotal
131 
132  if trueCandidates == 0 or allCandidates == 0:
133  continue
134 
135  timePerCandidate = format.duration(time / trueCandidates) + ' (' + format.duration(time / allCandidates) + ')'
136  timePercent = time / tt_particle * 100 if tt_particle > 0 else 0
137 
138  percents = [p.module_statistic.channel_time_per_module[channel.label].get(key, 0.0) / float(time) * 100.0
139  if time > 0 else 0.0 for key in moduleTypes[:-1]]
140  percents.append(100.0 - sum(percents))
141 
142  table.add(name=format.decayDescriptor(channel.label),
143  bargraph=r'\plotbar{ %g/, %g/, %g/, %g/, %g/, %g/, }' % tuple(percents),
144  time=format.duration(time),
145  timePerCandidate=timePerCandidate,
146  timePercent=time / tt_particle * 100 if p.total_time > 0 else 0)
147 
148  o += table.finish(tail='Total & & {tt_channel} / {tt_particle} & & {fraction:.2f}'.format(
149  tt_channel=format.duration(tt_channel), tt_particle=format.duration(tt_particle), fraction=fraction))
150 
151  for p in monitoringParticle:
152  print(p.particle.identifier)
153 
154  o += b2latex.Section(format.decayDescriptor(p.particle.identifier)).finish()
155  string = b2latex.String(r"In the reconstruction of {name} {nChannels} out of {max_nChannels} possible channels were used. "
156  r"The covered inclusive / exclusive branching fractions is {inc_br:.5f} / {exc_br:.5f}."
157  r"The final unique efficiency and purity was {eff:.5f} / {pur:.5f}")
158 
159  o += string.finish(name=format.decayDescriptor(p.particle.identifier),
160  nChannels=p.reconstructed_number_of_channels,
161  max_nChannels=p.total_number_of_channels,
162  exc_br=sum(p.exc_br_per_channel.values()),
163  inc_br=sum(p.inc_br_per_channel.values()),
164  eff=p.after_tag.efficiency,
165  pur=p.after_tag.purity)
166 
167  roc_plot_filename = monitoring.removeJPsiSlash(p.particle.identifier + '_ROC')
168  monitoring.MonitorROCPlot(p, roc_plot_filename)
169  o += b2latex.Graphics().add(roc_plot_filename + '.png', width=0.8).finish()
170 
171  diag_plot_filename = monitoring.removeJPsiSlash(p.particle.identifier + '_Diag')
172  monitoring.MonitorDiagPlot(p, diag_plot_filename)
173  o += b2latex.Graphics().add(diag_plot_filename + '.png', width=0.8).finish()
174 
175  if p.particle.identifier in ['B+:generic', 'B0:generic']:
176  money_plot_filename = monitoring.removeJPsiSlash(p.particle.identifier + '_Money')
177  monitoring.MonitorMbcPlot(p, money_plot_filename)
178  g = b2latex.Graphics()
179  for filename in glob.glob(money_plot_filename + '_*.png'):
180  g.add(filename, width=0.49)
181  o += g.finish()
182 
183  if p.particle.identifier in ['B+:semileptonic', 'B0:semileptonic']:
184  money_plot_filename = monitoring.removeJPsiSlash(p.particle.identifier + '_Money')
185  monitoring.MonitorCosBDLPlot(p, money_plot_filename)
186  g = b2latex.Graphics()
187  for filename in glob.glob(money_plot_filename + '_*.png'):
188  g.add(filename, width=0.49)
189  o += g.finish()
190 
191  table = b2latex.LongTable(columnspecs=r'c|rr|rrr',
192  caption='Per-channel efficiency before and after the applied pre-cut.',
193  head=r'Particle & \multicolumn{2}{c}{Covered BR} '
194  r' & \multicolumn{3}{c}{pre-cut} \\'
195  r' & exc & inc & user & ranking & vertex ',
196  format_string=r'{name} & {exc_br:.3f} & {inc_br:.3f} & {user_pre_cut:.5f} & '
197  r'{ranking_pre_cut:.5f} & {vertex_pre_cut:.5f}')
198 
199  for channel in p.particle.channels:
200  table.add(name=format.decayDescriptor(channel.label),
201  exc_br=p.exc_br_per_channel[channel.label],
202  inc_br=p.inc_br_per_channel[channel.label],
203  user_pre_cut=p.before_ranking[channel.label].efficiency,
204  ranking_pre_cut=p.after_ranking[channel.label].efficiency,
205  vertex_pre_cut=p.after_vertex[channel.label].efficiency,
206  absolute_post_cut=p.before_ranking_postcut.efficiency,
207  ranking_post_cut=p.after_ranking_postcut.efficiency,
208  after_tag=p.after_tag.efficiency)
209  o += table.finish()
210 
211  table = b2latex.LongTable(columnspecs=r'c|c|rrr',
212  caption='Per-channel purity before and after the applied pre-cut.',
213  head=r'Particle & Ignored '
214  r' & \multicolumn{3}{c}{pre-cut} \\'
215  r' && user & ranking & vertex ',
216  format_string=r'{name} & {ignored} & {user_pre_cut:.5f} & {ranking_pre_cut:.5f}'
217  r' & {vertex_pre_cut:.5f}')
218 
219  for channel in p.particle.channels:
220  table.add(name=format.decayDescriptor(channel.label),
221  ignored=r'\textcolor{red}{$\blacksquare$}' if p.ignored_channels[channel.label] else '',
222  user_pre_cut=p.before_ranking[channel.label].purity,
223  ranking_pre_cut=p.after_ranking[channel.label].purity,
224  vertex_pre_cut=p.after_vertex[channel.label].purity)
225  o += table.finish()
226 
227  o.save(output_file, compile=True)