ATLAS Offline Software
Loading...
Searching...
No Matches
SUSY_Helpers.py
Go to the documentation of this file.
1# Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
2
3# Python helper functions for SUSY event generation
4# written by Zach Marshall <zach.marshall@cern.ch>
5
6# Helper functions we need in MadGraphControl
7from MadGraphControl.MadGraphUtils import (generate, generate_from_gridpack,my_MGC_instance, add_lifetimes, arrange_output, MADGRAPH_GRIDPACK_LOCATION,is_gen_from_gridpack, new_process ) # noqa: F401
8
9
10# For moving files around
11import shutil
12
13# For checking for files on the disk
14import os
15
16# For the type hint on runArgs
17from PyJobTransformsCore.runargs import RunArguments
18
19# For logging, we'll build our own logger
20from AthenaCommon import Logging
21susylog = Logging.logging.getLogger('MadGraphUtilsSUSY')
22
24 """ Returns a static MadGraph particle definition-style list
25 of multi-particle definitions relevant for SUSY event generation
26 """
27
28 return """
29# Define multiparticle labels
30define p = g u c d s u~ c~ d~ s~
31define j = g u c d s u~ c~ d~ s~
32define pb = g u c d s b u~ c~ d~ s~ b~
33define jb = g u c d s b u~ c~ d~ s~ b~
34define l+ = e+ mu+
35define l- = e- mu-
36define vl = ve vm vt
37define vl~ = ve~ vm~ vt~
38define fu = u c e+ mu+ ta+
39define fu~ = u~ c~ e- mu- ta-
40define fd = d s ve~ vm~ vt~
41define fd~ = d~ s~ ve vm vt
42define susystrong = go ul ur dl dr cl cr sl sr t1 t2 b1 b2 ul~ ur~ dl~ dr~ cl~ cr~ sl~ sr~ t1~ t2~ b1~ b2~
43define susyweak = el- el+ er- er+ mul- mul+ mur- mur+ ta1- ta1+ ta2- ta2+ n1 n2 n3 n4 x1- x1+ x2- x2+ sve sve~ svm svm~ svt svt~
44define susylq = ul ur dl dr cl cr sl sr
45define susylq~ = ul~ ur~ dl~ dr~ cl~ cr~ sl~ sr~
46define susysq = ul ur dl dr cl cr sl sr t1 t2 b1 b2
47define susysq~ = ul~ ur~ dl~ dr~ cl~ cr~ sl~ sr~ t1~ t2~ b1~ b2~
48define susysl = el- el+ er- er+ mul- mul+ mur- mur+ ta1- ta1+ ta2- ta2+
49define susyv = sve svm svt
50define susyv~ = sve~ svm~ svt~
51"""
52
53
54def get_SUSY_variations( process: str, masses: dict[str, str | float], syst_mod: str | None = None, ktdurham: str | None = None ) -> float:
55 """
56 Provides varied matching scales for SUSY event generation based on inputs
57 Params:
58 process: the process to be generated (e.g. p p > go go). Used to figure
59 out which particles are relevant for the matching scale
60 masses: the dictionary of PDGID to mass mapping, used to figure out the
61 scale setting in GeV
62 syst_mod: the requested systematic variation (if any); can be msup,
63 msdw, or None
64 ktdurham: the matching scale in case the user provides it by hand
65 Returns:
66 the matching scale
67 """
68 # Don't override an explicit setting from the run card!
69 if ktdurham is None:
70 prod_particles = []
71 if process is not None:
72 id_map = {'go':'1000021','dl':'1000001','ul':'1000002','sl':'1000003','cl':'1000004','b1':'1000005','t1':'1000006',
73 'dr':'2000001','ur':'2000002','sr':'2000003','cr':'2000004','b2':'2000005','t2':'2000006',
74 'n1':'1000022','n2':'1000023','x1':'1000024','x2':'1000037','n3':'1000025','n4':'1000035',
75 'el':'1000011','mul':'1000013','ta1':'1000015','sve':'1000012','svm':'1000014','svt':'1000016',
76 'er':'2000011','mur':'2000013','ta2':'2000015'}
77 for l in process:
78 if 'generate' in l or 'add process' in l:
79 clean_proc = l.replace('generate','').replace('+','').replace('-','').replace('~','').replace('add process','').split('>')[1].split(',')[0]
80 for particle in clean_proc.split():
81 if particle not in id_map:
82 susylog.info(f'Particle {particle} not found in PDG ID map - skipping')
83 else:
84 prod_particles += id_map[particle]
85 # If we don't specify a process, then all we can do is guess based on available masses
86 # Same if we failed to identify the right particles
87 my_mass = 10000.
88 if len(prod_particles)>0:
89 for x in prod_particles:
90 if x in masses:
91 my_mass = min(my_mass,abs(float(masses[x])))
92 else:
93 susylog.info(f'Seem to ask for production of PDG ID {x}, but {x} not in mass dictionary?')
94 if my_mass>9999.:
95 strong_ids = ['1000001','1000002','1000003','1000004','1000005','1000006','2000001','2000002','2000003','2000004','2000005','2000006','1000021']
96 weak_ids = ['1000023','1000024','1000025','1000011','1000013','1000015','2000011','2000013','2000015','1000012','1000014','1000016']
97 # First check the lightest of the heavy sparticles - all squarks and gluino
98 my_mass = min([abs(float(masses[x])) for x in strong_ids if x in masses])
99 # Now check if strong production was not the key mode
100 if my_mass>10000.:
101 # This is a little tricky, but: we want the heaviest non-decoupled mass
102 my_mass = max([abs(float(masses[x])) for x in weak_ids if x in masses and float(masses[x])<10000.])
103 # Final check for N1N1 with everything else decoupled
104 if my_mass>10000. and '1000022' in masses:
105 my_mass = masses['1000022']
106 if my_mass>10000.:
107 raise RuntimeError('Could not understand which mass to use for matching cut in '+str(masses))
108
109 # Now set the matching scale accordingly
110 ktdurham = min(my_mass*0.25,500)
111 # Should not be weirdly low - can't imagine a situation where you'd really want the scale below 15 GeV
112 ktdurham = max(ktdurham,15)
113 if syst_mod == 'msup':
114 susylog.info('Applying upward variation (by 2x) of matching scale')
115 ktdurham = ktdurham*2.
116 elif syst_mod == 'msdw':
117 susylog.info('Applying downward variation (by 2x) of matching scale')
118 ktdurham = ktdurham*0.5
119
120 susylog.info('For matching, will use ktdurham of '+str(ktdurham))
121
122 return abs(ktdurham)
123
124
125
126def SUSY_process(process: str = '') -> str:
127 '''
128 Helper function to turn a simplified model process definition into
129 a full, MG5_aMC-compliant process definition
130 '''
131
132 # Generate the new process!
133 if 'import model' in process:
134 susylog.info('Assuming that you have specified the model in your process string already')
135 full_proc = ''
136 for l in process.split('\n'):
137 if 'import model' in l:
138 full_proc += l+'\n'
139 break
140 # Only magically add helpful definitions if we are in the right model
141 if 'MSSM_SLHA2' in full_proc:
142 full_proc+=helpful_SUSY_definitions()
143 for l in process.split('\n'):
144 if 'import model' not in l:
145 full_proc += l+'\n'
146 full_proc+="""
147# Output processes to MadEvent directory
148output -f
149"""
150 else:
151 full_proc = "import model MSSM_SLHA2\n"+helpful_SUSY_definitions()+"""
152# Specify process(es) to run
153
154"""+process+"""
155# Output processes to MadEvent directory
156output -f
157"""
158 return full_proc
159
160
161def SUSY_Generation(runArgs: RunArguments | None = None, process: str | None = None, plugin: str | None = None,\
162 syst_mod: str | None = None, keepOutput: bool = False, param_card: str | None = None,\
163 writeGridpack: bool = False, madspin_card: str | None = None, run_settings: dict = {},
164 params: dict = {}, fixEventWeightsForBridgeMode: bool = False,\
165 add_lifetimes_lhe: bool = False, usePMGSettings: bool = True) -> float:
166
167 """
168 Helper function for SUSY event generation, used in simplified model setups. Attempts to
169 simplify and harmonize a bunch of the things that normally have to be done by hand.
170 Keyword Arguments:
171 runArgs: passed from the job transform, includes random number seed, ecm energy, and setting for output files
172 process: simplified process definition for event generation
173 plugin: in case a MG5_aMC plugin is requested for event generation
174 syst_mod: requested systematic variation of parameter settings
175 keepOutput: retains the process directory created by MG5_aMC. Should always be false in production
176 param_card: In case an explicit parameter card is needed (e.g. for pMSSM), can be set this way
177 writeGridpack: used to construct gridpacks
178 madspin_card: used for MadSpin definitions
179 run_settings: any additional run card settings required (e.g. cuts on particle momenta). Dictionary to be
180 passed into modify_run_card(...)
181 params: any additional param card settings. Dictionary to be passed into modify_param_card(...)
182 fixEventWeightsForBridgeMode: In case Bridge mode is being used, this applies a fix to the event weights
183 add_lifetimes_lhe: if fundamental particles written to the LHE have a lifetime (e.g. sleptons or charginos), this
184 allows the inclusion of their time of flight lifetime in the LHE file directly
185 usePMGSettings (bool): See :py:func:`new_process`. Will set SM parameters to the appropriate values. Default: True.
186 returns:
187 the setting of the matching scale to be provided to Pythia8
188 """
189
190 global my_MGC_instance # noqa: F824
191 ktdurham = run_settings['ktdurham'] if 'ktdurham' in run_settings else None
192 ktdurham = get_SUSY_variations( process, params['MASS'] , syst_mod , ktdurham=ktdurham )
193
194 process_dir = MADGRAPH_GRIDPACK_LOCATION
195 if not is_gen_from_gridpack():
196 full_proc = SUSY_process(process)
197 process_dir = new_process(full_proc, plugin=plugin, usePMGSettings=usePMGSettings)
198 susylog.info('Using process directory '+str(process_dir))
199
200 # Grab the param card and move the new masses into place
201 my_MGC_instance.paramCard.modify_paramCardDict(params=params)
202
203 # Set up the extras dictionary
204 settings = {'ktdurham':ktdurham}
205 settings.update(run_settings) # This allows explicit settings in the input to override these settings
206
207 # Set up the run card
208 my_MGC_instance.runCardDict.update(settings)
209
210 # Set up madspin if needed
211 if madspin_card is not None:
212 if not os.access(madspin_card,os.R_OK):
213 raise RuntimeError('Could not locate madspin card at '+str(madspin_card))
214 shutil.copy(madspin_card,process_dir+'/Cards/madspin_card.dat')
215
216 # Generate events!
217 if is_gen_from_gridpack():
218 generate_from_gridpack(runArgs=runArgs)
219 else:
220 # Grab the run card and move it into place
221 generate(runArgs=runArgs,process_dir=process_dir,grid_pack=writeGridpack)
222
223 # Add lifetimes to LHE before arranging output if requested
224 if add_lifetimes_lhe :
225 susylog.info('Requested addition of lifetimes to LHE files: doing so now.')
226 if is_gen_from_gridpack():
227 add_lifetimes()
228 else:
229 add_lifetimes(process_dir=process_dir)
230
231 # Move output files into the appropriate place, with the appropriate name
232 arrange_output(process_dir=process_dir,saveProcDir=keepOutput,runArgs=runArgs,fixEventWeightsForBridgeMode=fixEventWeightsForBridgeMode)
233
234 susylog.info('All done generating events!!')
235 return settings['ktdurham']
236
#define min(a, b)
Definition cfImp.cxx:40
#define max(a, b)
Definition cfImp.cxx:41
std::string replace(std::string s, const std::string &s2, const std::string &s3)
Definition hcg.cxx:312
std::vector< std::string > split(const std::string &s, const std::string &t=":")
Definition hcg.cxx:179
float get_SUSY_variations(str process, dict[str, str|float] masses, str|None syst_mod=None, str|None ktdurham=None)
str SUSY_process(str process='')
float SUSY_Generation(RunArguments|None runArgs=None, str|None process=None, str|None plugin=None, str|None syst_mod=None, bool keepOutput=False, str|None param_card=None, bool writeGridpack=False, str|None madspin_card=None, dict run_settings={}, dict params={}, bool fixEventWeightsForBridgeMode=False, bool add_lifetimes_lhe=False, bool usePMGSettings=True)