Belle II Software  light-2403-persian
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=f'Total & & {format.duration(tt_channel)} / {format.duration(tt_particle)} & & {fraction:.2f}')
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', 'B_s0: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=False)