Belle II Software development
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
24from fei import monitoring
25
26from B2Tools import b2latex
27from B2Tools import format
28
29import sys
30import glob
31
32
33if __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)