3
################################################################################
5
# Richard Corke (richard.corke@thep.lu.se)
7
# Changed structure to not read entire files into memory
9
# Based on merge.pl v1.2 by Michel Herquet (UCL-CP3)
10
# DESCRIPTION : script to merge to LHE events files
12
# ./merge.pl eventfile1.lhe.gz eventfile2.lhe.gz ... result.lhe.gz banner.txt
13
# Note that result can be eventfile1, eventfile2, ...
14
# OUTPUT : merged file, banner
15
################################################################################
18
use List::Util qw[min max];
21
###########################################################################
23
###########################################################################
24
# Set tag names for later
25
my $begin_header = '<header>';
26
my $end_header = '</header>';
27
my $begin_event = '<event>';
28
my $end_event = '</event>';
29
my $begin_init = '<init>';
30
my $end_init = '</init>';
32
# Parse the command line arguments
34
die "This script must be called with at least three filenames as arguments!\n";
36
my $bannerfile = pop(@ARGV);
37
my $outfile = pop(@ARGV);
40
###########################################################################
41
# First pass - extract number of events and cross-sections
42
###########################################################################
43
foreach $infile (@ARGV) {
44
$gzin = gzopen($infile, "r") || die ("Couldn't open file $infile\n");
46
# No. events and cross-section from current file
47
$noevents = $xsec = -1;
49
# Storage for current file's init block
52
# Keep track if we are in the init block or not
56
$gzbytes = $gzin->gzreadline($gzline);
57
if ($gzbytes == -1) { die("Error reading from file $infile\n"); }
58
if ($gzbytes == 0) { last; }
60
# Extract <MGGenerationInfo> information
61
if ($initblock == 0) {
62
if ($gzline =~ s/# Number of Events\s*:\s*(.*)\n/$1/) { $noevents = $gzline; }
63
if ($gzline =~ s/# Integrated weight \(pb\)\s*:\s*(.*)\n/$1/) { $xsec = $gzline; }
65
# Check if we enter <init> block
66
if ($gzline =~ m/$begin_init/) { $initblock++; next; }
68
# Extract <init> information
71
# Check for end of <init> block
72
if ($gzline =~ m/$end_init/) { last; }
74
# Remove leading whitespace and split
76
@gzparam = split(/\s+/, $gzline);
78
# Check <init> block line has right no. of entries
79
if ($initblock == 1) {
80
if ($#gzparam != 9) { die "Not right number of params in init"; }
82
if ($#gzparam != 3) { die "Not right number of params in init"; }
85
push(@gzinit, [ @gzparam ]);
92
# Check the file contained all the information we need
93
if ($noevents == -1 || $xsec == -1) {
94
die("Couldn't extract No. Events / Cross-section from $infile")
97
# Store information for later
98
push(@infiles, [ $infile, $noevents, $xsec, [ @gzinit ] ]);
102
###########################################################################
103
# Check/compute cross-sections, totals and unit weight
104
###########################################################################
105
$oldxsec = $infiles[0][2];
106
@oldinit = @{$infiles[0][3]};
107
$totevents = 0; $totxsec = 0.0;
108
foreach $infile (@infiles) {
109
print "Input file: $infile->[0]\n";
110
print " No. Events = $infile->[1], Cross-section = $infile->[2]\n";
112
# Check that cross sections do not differ too much
113
$newxsec = $infile->[2];
114
if (abs(($newxsec - $oldxsec) / $newxsec) > 0.05 ) {
115
print " WARNING Cross sections do not agree with a 5\% precision!\n";
119
@currinit = @{$infile->[3]};
120
# Same number of lines in <init> block?
121
if ($#oldinit != $#currinit) { die("Init blocks do not match"); }
123
# Same number of entries on first line of <init> block?
124
if ($#{$oldinit[0]} != $#{$currinit[0]}) { die("Init blocks do not match"); }
126
# All entries the same on first line of <init> block?
127
for ($i = 0; $i <= $#{$oldinit[0]}; $i++) {
128
if ($oldinit[0][$i] != $currinit[0][$i])
129
{ die("Init blocks do not match"); }
132
# Create new init block (overwrite first file's init block data)
133
for ($i = 1; $i <= $#oldinit; $i++) {
134
if ($oldinit[$i][3] != $currinit[$i][3]) { die("Init blocks do not match"); }
136
print " xsecup = $currinit[$i][0], xerrup = $currinit[$i][1]\n";
137
print " xmaxup = $currinit[$i][2], lprup = $currinit[$i][3]\n";
139
# XSECUP = sum(xsecup * no.events) / tot.events
140
# XERRUP = sqrt( sum(sigma^2 * no.events^2) ) / tot.events
142
# Here we temporarily store:
143
# sum(xsecup * no.events)
144
# sum(sigma^2 * no.events^2)
145
if (\$oldinit == \$currinit) {
146
$oldinit[$i][0] *= $infile->[1];
147
$oldinit[$i][1] *= $oldinit[$i][1] * $infile->[1]**2;
150
$oldinit[$i][0] += ($currinit[$i][0] * $infile->[1]);
151
$oldinit[$i][1] += $currinit[$i][1]**2 * $infile->[1]**2;
155
# XMAXUP = max(xmaxup)
156
$oldinit[$i][2] = max($oldinit[$i][2], $currinit[$i][2]);
159
# Total number of events and total cross-section
160
$totevents += $infile->[1];
161
$totxsec += ($infile->[1] * $infile->[2]);
167
# Finish calculation of XSECUP and XERRUP
168
for ($i = 1; $i <= $#oldinit; $i++) {
169
$oldinit[$i][0] /= $totevents;
170
$oldinit[$i][0] = sprintf('%0.5E', $oldinit[$i][0]);
172
$oldinit[$i][1] = sqrt($oldinit[$i][1]);
173
$oldinit[$i][1] /= $totevents;
174
$oldinit[$i][1] = sprintf('%0.5E', $oldinit[$i][1]);
177
# Finish calculation of total xsec and new unit weight
178
$totxsec /= $totevents;
179
$dispxsec = sprintf('%0.5E', $totxsec);
180
$uwgt = sprintf('%0.5E', $totxsec / $totevents);
182
# Display new information
183
print "Banner file: $bannerfile\n";
184
print "Output file: $outfile\n";
185
print " No. Events = $totevents, Cross-section = $dispxsec\n";
186
for ($i = 1; $i <= $#oldinit; $i++) {
187
print " xsecup = $oldinit[$i][0], xerrup = $oldinit[$i][1]\n";
188
print " xmaxup = $oldinit[$i][2], lprup = $oldinit[$i][3]\n";
191
print " Unit weight = $uwgt\n";
194
###########################################################################
195
# Second pass - output file with new information
196
###########################################################################
197
# The first file is written out with changes to the header, init block
198
# and unit weight of each event.
199
# All events from other files are then written out with their unit weight
201
###########################################################################
203
$gzout = gzopen($outfile, "w") || die ("Couldn't open file $outfile\n");
204
open(BANNER, ">$bannerfile") || die ("Couldn't open file $outfile\n");
208
foreach $infile (@infiles) {
209
$gzin = gzopen($infile->[0], "r") || die ("Couldn't open file $infile\n");
212
$gzbytes = $gzin->gzreadline($gzline);
213
if ($gzbytes == -1) { die("Error reading from file $infile\n"); }
214
if ($gzbytes == 0) { last; }
218
if ($gzline =~ m/$begin_header/) { $stage++; }
220
# Header (and output to banner)
221
} elsif ($stage == 1) {
222
$gzline =~ s/# Integrated weight \(pb\)\s*:(.*)\n/# Integrated weight (pb) : $dispxsec\n/;
223
$gzline =~ s/# Number of Events\s*:(.*)\n/# Number of Events : $totevents\n/;
224
$gzline =~ s/# Unit wgt\s*:(.*)\n/# Unit wgt : $uwgt\n/;
226
if ($gzline =~ m/$end_header/) { $stage++; }
227
else { print BANNER $gzline; }
230
} elsif ($stage == 2) {
231
if ($gzline =~ m/$end_init/) {
232
$gzline = "<init>\n";
234
for ($i = 0; $i <= $#oldinit; $i++) {
236
for ($j = 0; $j <= $#{$oldinit[$i]}; $j++) {
237
$gzline .= "$oldinit[$i][$j] ";
242
$gzline .= "</init>\n";
248
} elsif ($stage == 3) {
249
if ($gzline =~ m/$begin_event/) { $stage++; } else { next; }
252
} elsif ($stage == 4) {
254
@gzparam = split(/\s+/, $gzline);
255
if ($#gzparam != 5) { die "Not right number of param in first line of event"; }
256
$gzline = " $gzparam[0] $gzparam[1] $uwgt $gzparam[3] $gzparam[4] $gzparam[5]\n";
261
} elsif ($stage == 5) {
262
if ($gzline =~ m/$end_event/) { $stage = 3; $eventcount++ }
267
$gzout->gzwrite($gzline);
271
# Write out closing tag and close
272
$gzout->gzwrite("</LesHouchesEvents>\n");
275
print "Wrote $eventcount events\n";