ATLAS Offline Software
Loading...
Searching...
No Matches
LheConverterUpc.py
Go to the documentation of this file.
1# Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
2
3import os
4import re
5import math
6import xml.dom.minidom
7
8from AthenaPython.PyAthena import StatusCode
9from AthenaPython.PyAthena import Alg
10
11class LheConverterUpc(Alg):
12 '''
13 Class for modifying output LHE file from Superchic and Madgraph + ensuring compatibility with Tauola
14 Intended for ultraperipheral collision (UPC) processes i.e. y y -> l+ l-
15 This code also contains a hack that was present in the earlier version in the Generators/Superchic_i/python/LheConverterTauolaPhotonHack.py
16 The hack was recommended by the Tauola authors, intended for the gamma gamma -> tau+ tau- process
17 The hack changes the PDG ID of the initial state particles, from photons to electrons
18 '''
19
20 def __init__(self, name='LheConverterUpc', generator='Superchic', mode='Pythia8', energy=5020):
21 super(LheConverterUpc, self).__init__(name=name)
22 self.generator = generator # options: 'Superchic' (default), 'Madgraph5'
23 self.mode = mode # options: 'Pythia8' (default), 'Tauolapp'
24 self.energy = energy # energy read via JOs from runArg
25
26 outFileName = 'events.lhe'
27 done = False
28 leptons = ['11', '-11', '13', '-13', '15', '-15'] # e, µ, τ
29
30 def initialize(self):
31
32 self.inFileName = self.fileName
33
34 if self.inFileName==self.outFileName:
35 import shutil
36 shutil.move('./events.lhe','events.lhe_tmpconverter')
37 self.inFileName = 'events.lhe_tmpconverter'
38 if(os.path.isfile(self.inFileName)):
40 return self.convert()
41 else:
42 return StatusCode.Failure
43
44 def convert(self):
45 '''
46 Modifies `events.lhe` output file from Madgraph by doing the following:
47 - Replaces init block
48 - Changes photon px and py to zero and energy to pz
49 - Recalculates energy scales as an average of lepton pair transverse momentum
50 '''
51 if not self.done:
52 DOMTree = xml.dom.minidom.parse(self.inFileName)
53 collection = DOMTree.documentElement
54
55 # Replace init block
56 init = collection.getElementsByTagName('init')
57
58 # LHE requires very strict format of the <init> block
59 l_init_repl1=["13","-13","2.510000e+03","2.510000e+03","0","0","0","0","3","1"]
60 str_init_repl2 =" 1.000000e+00 0.000000e+00 1.000000e+00 9999"
61 l_init_repl1[2]='%.6e' %(self.energy/2.)
62 l_init_repl1[3]='%.6e' %(self.energy/2.)
63
64 delimiter = " " # Define a delimiter
65 str_init_repl1 = map(str, l_init_repl1)
66 jstr_init_repl1 = delimiter.join(str_init_repl1)
67 jstr_init_repl1 = '\n ' + jstr_init_repl1 + '\n'
68 jstr_init_repl = jstr_init_repl1 + str_init_repl2 + '\n'
69 init[0].firstChild.data = jstr_init_repl
70
71 # The comment line below indicates which part of the regex grabs the parton-level information that's to be modified. The index in list(header.groups()) is also shown
72 # energy scale [1]
73 event_header = r'^(\s*\S*\s*\S*\s*\S*\s*)(\S*)(.*)$'
74
75 # The comment line below indicates which part of the regex grabs the parton-level information that's to be modified. The index in list(particle) is also shown
76 # pdgid [1] px [3] py [5] pz [7] e [9]
77 event_particle = r'^(\s*)([0-9-]+)(\s+[0-9-]+\s+[0-9-]+\s+[0-9-]+\s+[0-9-]+\s+[0-9-]+\s+)(\S+)(\s+)(\S+)(\s+)(\S+)(\s+)(\S+)(.*)$'
78
79 events = collection.getElementsByTagName('event')
80 for i, event in enumerate(events):
81
82 # Remove the <mgrwt> block in case of Madgraph (not needed in UPC)
83 nodes = event.getElementsByTagName('mgrwt')
84 for node in nodes:
85 parent = node.parentNode
86 parent.removeChild(node)
87
88 new_particles = []
89 lepton_mom = []
90
91 particles = re.findall(event_particle, event.firstChild.data, re.MULTILINE)
92 for particle in particles:
93 particle = list(particle)
94 if particle[1] == '22': # photon
95 # Zero photon transverse momentum, change energy to be equal pz
96 particle[3] = '0.'
97 particle[5] = '0.'
98 particle[9] = particle[7]
99 # If Tauolapp requested, change PID of photons to electrons (hack recommended by Tauola authors)
100 if self.mode == 'Tauolapp':
101 if i%2 == 0:
102 particle[1] = '11' if float(particle[7]) > 0.0 else '-11'
103 else:
104 particle[1] = '-11' if float(particle[7]) > 0.0 else '11'
105 new_particles.append(''.join(particle))
106 elif particle[1] in self.leptons:
107 # Read leptons px and py
108 lepton_mom.append(float(particle[3]))
109 lepton_mom.append(float(particle[5]))
110 new_particles.append(''.join(particle))
111 else:
112 new_particles.append(''.join(particle))
113
114 # Calculate new energy scale
115 l1_px, l1_py, l2_px, l2_py = lepton_mom
116 l1_pt = math.sqrt(l1_px**2 + l1_py**2)
117 l2_pt = math.sqrt(l2_px**2 + l2_py**2)
118 energy_scale = f'{(l1_pt + l2_pt) / 2.:.9E}'
119
120 header = re.search(event_header, event.firstChild.data, re.MULTILINE)
121 header = list(header.groups())
122 header[1] = energy_scale
123
124 if self.generator == 'Superchic':
125 event.firstChild.data = '\n'.join([''.join(header)] + new_particles) + '\n'
126 if self.generator == 'Madgraph5':
127 event.firstChild.data = '\n'.join([''.join(header)] + new_particles)
128
129 with open(self.outFileName, 'w') as output:
130 output.write(DOMTree.toxml().replace('<?xml version="1.0" ?>', ' '))
131
132 self.done = True
133
134 return StatusCode.Success
135
if(pathvar)
void print(char *figname, TCanvas *c1)
STL class.
__init__(self, name='LheConverterUpc', generator='Superchic', mode='Pythia8', energy=5020)
std::string replace(std::string s, const std::string &s2, const std::string &s3)
Definition hcg.cxx:312
void initialize()