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