Package madgraph :: Package madevent :: Module gen_ximprove
[hide private]
[frames] | no frames]

Source Code for Module madgraph.madevent.gen_ximprove

   1  ################################################################################ 
   2  # 
   3  # Copyright (c) 2014 The MadGraph5_aMC@NLO Development team and Contributors 
   4  # 
   5  # This file is a part of the MadGraph5_aMC@NLO project, an application which  
   6  # automatically generates Feynman diagrams and matrix elements for arbitrary 
   7  # high-energy processes in the Standard Model and beyond. 
   8  # 
   9  # It is subject to the MadGraph5_aMC@NLO license which should accompany this  
  10  # distribution. 
  11  # 
  12  # For more information, visit madgraph.phys.ucl.ac.be and amcatnlo.web.cern.ch 
  13  # 
  14  ################################################################################ 
  15  """ A python file to replace the fortran script gen_ximprove. 
  16      This script analyses the result of the survey/ previous refine and  
  17      creates the jobs for the following script. 
  18  """ 
  19  from __future__ import division 
  20   
  21  from __future__ import absolute_import 
  22  import collections 
  23  import os 
  24  import glob 
  25  import logging 
  26  import math 
  27  import re 
  28  import subprocess 
  29  import shutil 
  30  import stat 
  31  import sys 
  32  import six 
  33  from six.moves import range 
  34  from six.moves import zip 
  35   
  36  try: 
  37      import madgraph 
  38  except ImportError: 
  39      MADEVENT = True 
  40      import internal.sum_html as sum_html 
  41      import internal.banner as bannermod 
  42      import internal.misc as misc 
  43      import internal.files as files 
  44      import internal.cluster as cluster 
  45      import internal.combine_grid as combine_grid 
  46      import internal.combine_runs as combine_runs 
  47      import internal.lhe_parser as lhe_parser 
  48      if six.PY3: 
  49          import internal.hel_recycle as hel_recycle 
  50  else: 
  51      MADEVENT= False 
  52      import madgraph.madevent.sum_html as sum_html 
  53      import madgraph.various.banner as bannermod 
  54      import madgraph.various.misc as misc 
  55      import madgraph.iolibs.files as files 
  56      import madgraph.various.cluster as cluster 
  57      import madgraph.madevent.combine_grid as combine_grid 
  58      import madgraph.madevent.combine_runs as combine_runs 
  59      import madgraph.various.lhe_parser as lhe_parser 
  60      if six.PY3: 
  61          import madgraph.madevent.hel_recycle as hel_recycle 
  62   
  63  logger = logging.getLogger('madgraph.madevent.gen_ximprove') 
  64  pjoin = os.path.join 
65 66 -class gensym(object):
67 """a class to call the fortran gensym executable and handle it's output 68 in order to create the various job that are needed for the survey""" 69 70 #convenient shortcut for the formatting of variable 71 @ staticmethod
72 - def format_variable(*args):
73 return bannermod.ConfigFile.format_variable(*args)
74 75 combining_job = 2 # number of channel by ajob 76 splitted_grid = False 77 min_iterations = 3 78 mode= "survey" 79 80
81 - def __init__(self, cmd, opt=None):
82 83 try: 84 super(gensym, self).__init__(cmd, opt) 85 except TypeError: 86 pass 87 88 # Run statistics, a dictionary of RunStatistics(), with 89 self.run_statistics = {} 90 91 self.cmd = cmd 92 self.run_card = cmd.run_card 93 self.me_dir = cmd.me_dir 94 95 96 # dictionary to keep track of the precision when combining iteration 97 self.cross = collections.defaultdict(int) 98 self.abscross = collections.defaultdict(int) 99 self.sigma = collections.defaultdict(int) 100 self.chi2 = collections.defaultdict(int) 101 102 self.splitted_grid = False 103 if self.cmd.proc_characteristics['loop_induced']: 104 nexternal = self.cmd.proc_characteristics['nexternal'] 105 self.splitted_grid = max(2, (nexternal-2)**2) 106 if hasattr(self.cmd, "opts") and self.cmd.opts['accuracy'] == 0.1: 107 self.cmd.opts['accuracy'] = 0.02 108 109 if isinstance(cmd.cluster, cluster.MultiCore) and self.splitted_grid > 1: 110 self.splitted_grid = int(cmd.cluster.nb_core**0.5) 111 if self.splitted_grid == 1 and cmd.cluster.nb_core >1: 112 self.splitted_grid = 2 113 114 #if the user defines it in the run_card: 115 if self.run_card['survey_splitting'] != -1: 116 self.splitted_grid = self.run_card['survey_splitting'] 117 if self.run_card['survey_nchannel_per_job'] != 1 and 'survey_nchannel_per_job' in self.run_card.user_set: 118 self.combining_job = self.run_card['survey_nchannel_per_job'] 119 elif self.run_card['hard_survey'] > 1: 120 self.combining_job = 1 121 122 123 self.splitted_Pdir = {} 124 self.splitted_for_dir = lambda x,y: self.splitted_grid 125 self.combining_job_for_Pdir = lambda x: self.combining_job 126 self.lastoffset = {}
127 128 done_warning_zero_coupling = False
129 - def get_helicity(self, to_submit=True, clean=True):
130 """launch a single call to madevent to get the list of non zero helicity""" 131 132 self.subproc = [l.strip() for l in open(pjoin(self.me_dir,'SubProcesses', 133 'subproc.mg'))] 134 subproc = self.subproc 135 P_zero_result = [] 136 nb_tot_proc = len(subproc) 137 job_list = {} 138 139 140 for nb_proc,subdir in enumerate(subproc): 141 self.cmd.update_status('Compiling for process %s/%s.' % \ 142 (nb_proc+1,nb_tot_proc), level=None) 143 144 subdir = subdir.strip() 145 Pdir = pjoin(self.me_dir, 'SubProcesses',subdir) 146 logger.info(' %s ' % subdir) 147 148 self.cmd.compile(['madevent_forhel'], cwd=Pdir) 149 if not os.path.exists(pjoin(Pdir, 'madevent_forhel')): 150 raise Exception('Error make madevent_forhel not successful') 151 152 if not os.path.exists(pjoin(Pdir, 'Hel')): 153 os.mkdir(pjoin(Pdir, 'Hel')) 154 ff = open(pjoin(Pdir, 'Hel', 'input_app.txt'),'w') 155 ff.write('1000 1 1 \n 0.1 \n 2\n 0\n -1\n 1\n') 156 ff.close() 157 else: 158 try: 159 os.remove(pjoin(Pdir, 'Hel','results.dat')) 160 except Exception: 161 pass 162 # Launch gensym 163 p = misc.Popen(['../madevent_forhel < input_app.txt'], stdout=subprocess.PIPE, 164 stderr=subprocess.STDOUT, cwd=pjoin(Pdir,'Hel'), shell=True) 165 #sym_input = "%(points)d %(iterations)d %(accuracy)f \n" % self.opts 166 (stdout, _) = p.communicate(" ".encode()) 167 stdout = stdout.decode('ascii') 168 if os.path.exists(pjoin(self.me_dir,'error')): 169 raise Exception(pjoin(self.me_dir,'error')) 170 # note a continue is not enough here, we have in top to link 171 # the matrixX_optim.f to matrixX_orig.f to let the code to work 172 # after this error. 173 174 if 'no events passed cuts' in stdout: 175 raise Exception 176 177 all_zamp = set() 178 all_hel = set() 179 zero_gc = list() 180 all_zampperhel = set() 181 all_bad_amps_perhel = set() 182 183 for line in stdout.splitlines(): 184 if 'GC_' in line: 185 lsplit = line.split() 186 if float(lsplit[2]) ==0 == float(lsplit[3]): 187 zero_gc.append(lsplit[0]) 188 if 'Matrix Element/Good Helicity:' in line: 189 all_hel.add(tuple(line.split()[3:5])) 190 if 'Amplitude/ZEROAMP:' in line: 191 all_zamp.add(tuple(line.split()[1:3])) 192 if 'HEL/ZEROAMP:' in line: 193 nb_mat, nb_hel, nb_amp = line.split()[1:4] 194 if (nb_mat, nb_hel) not in all_hel: 195 continue 196 if (nb_mat,nb_amp) in all_zamp: 197 continue 198 all_zampperhel.add(tuple(line.split()[1:4])) 199 200 if zero_gc and not gensym.done_warning_zero_coupling: 201 gensym.done_warning_zero_coupling = True 202 logger.warning("The optimizer detects that you have coupling evaluated to zero: \n"+\ 203 "%s\n" % (' '.join(zero_gc)) +\ 204 "This will slow down the computation. Please consider using restricted model:\n" +\ 205 "https://answers.launchpad.net/mg5amcnlo/+faq/2312") 206 207 208 all_good_hels = collections.defaultdict(list) 209 for me_index, hel in all_hel: 210 all_good_hels[me_index].append(int(hel)) 211 212 #print(all_hel) 213 if self.run_card['hel_zeroamp']: 214 all_bad_amps = collections.defaultdict(list) 215 for me_index, amp in all_zamp: 216 all_bad_amps[me_index].append(int(amp)) 217 218 all_bad_amps_perhel = collections.defaultdict(list) 219 for me_index, hel, amp in all_zampperhel: 220 all_bad_amps_perhel[me_index].append((int(hel),int(amp))) 221 222 elif all_zamp: 223 nb_zero = sum(int(a[1]) for a in all_zamp) 224 if zero_gc: 225 logger.warning("Those zero couplings lead to %s Feynman diagram evaluated to zero (on 10 PS point),\n" % nb_zero +\ 226 "This part can optimize if you set the flag hel_zeroamp to True in the run_card."+\ 227 "Note that restricted model will be more optimal.") 228 else: 229 logger.warning("The optimization detected that you have %i zero matrix-element for this SubProcess: %s.\n" % nb_zero +\ 230 "This part can optimize if you set the flag hel_zeroamp to True in the run_card.") 231 232 #check if we need to do something and write associate information" 233 data = [all_hel, all_zamp, all_bad_amps_perhel] 234 if not self.run_card['hel_zeroamp']: 235 data[1] = '' 236 if not self.run_card['hel_filtering']: 237 data[0] = '' 238 data = str(data) 239 if os.path.exists(pjoin(Pdir,'Hel','selection')): 240 old_data = open(pjoin(Pdir,'Hel','selection')).read() 241 if old_data == data: 242 continue 243 244 245 with open(pjoin(Pdir,'Hel','selection'),'w') as fsock: 246 fsock.write(data) 247 248 249 for matrix_file in misc.glob('matrix*orig.f', Pdir): 250 251 split_file = matrix_file.split('/') 252 me_index = split_file[-1][len('matrix'):-len('_orig.f')] 253 254 basename = split_file[-1].replace('orig', 'optim') 255 split_out = split_file[:-1] + [basename] 256 out_file = pjoin('/', '/'.join(split_out)) 257 258 basename = 'template_%s' % split_file[-1].replace("_orig", "") 259 split_templ = split_file[:-1] + [basename] 260 templ_file = pjoin('/', '/'.join(split_templ)) 261 262 # Convert to sorted list for reproducibility 263 #good_hels = sorted(list(good_hels)) 264 good_hels = [str(x) for x in sorted(all_good_hels[me_index])] 265 if self.run_card['hel_zeroamp']: 266 267 bad_amps = [str(x) for x in sorted(all_bad_amps[me_index])] 268 bad_amps_perhel = [x for x in sorted(all_bad_amps_perhel[me_index])] 269 else: 270 bad_amps = [] 271 bad_amps_perhel = [] 272 if __debug__: 273 mtext = open(matrix_file).read() 274 nb_amp = int(re.findall('PARAMETER \(NGRAPHS=(\d+)\)', mtext)[0]) 275 logger.debug('nb_hel: %s zero amp: %s bad_amps_hel: %s/%s', len(good_hels),len(bad_amps),len(bad_amps_perhel), len(good_hels)*nb_amp ) 276 if len(good_hels) == 1: 277 files.cp(matrix_file, matrix_file.replace('orig','optim')) 278 continue # avoid optimization if onlye one helicity 279 recycler = hel_recycle.HelicityRecycler(good_hels, bad_amps, bad_amps_perhel) 280 # In case of bugs you can play around with these: 281 recycler.hel_filt = self.run_card['hel_filtering'] 282 recycler.amp_splt = self.run_card['hel_splitamp'] 283 recycler.amp_filt = self.run_card['hel_zeroamp'] 284 285 recycler.set_input(matrix_file) 286 recycler.set_output(out_file) 287 recycler.set_template(templ_file) 288 recycler.generate_output_file() 289 del recycler 290 291 # with misc.chdir(): 292 # pass 293 294 #files.ln(pjoin(Pdir, 'madevent_forhel'), Pdir, name='madevent') ##to be removed 295 296 return {}, P_zero_result
297 298
299 - def launch(self, to_submit=True, clean=True):
300 """ """ 301 302 if not hasattr(self, 'subproc'): 303 self.subproc = [l.strip() for l in open(pjoin(self.me_dir,'SubProcesses', 304 'subproc.mg'))] 305 subproc = self.subproc 306 307 P_zero_result = [] # check the number of times where they are no phase-space 308 309 nb_tot_proc = len(subproc) 310 job_list = {} 311 for nb_proc,subdir in enumerate(subproc): 312 self.cmd.update_status('Compiling for process %s/%s. <br> (previous processes already running)' % \ 313 (nb_proc+1,nb_tot_proc), level=None) 314 315 subdir = subdir.strip() 316 Pdir = pjoin(self.me_dir, 'SubProcesses',subdir) 317 logger.info(' %s ' % subdir) 318 319 # clean previous run 320 if clean: 321 for match in misc.glob('*ajob*', Pdir): 322 if os.path.basename(match)[:4] in ['ajob', 'wait', 'run.', 'done']: 323 os.remove(match) 324 for match in misc.glob('G*', Pdir): 325 if os.path.exists(pjoin(match,'results.dat')): 326 os.remove(pjoin(match, 'results.dat')) 327 if os.path.exists(pjoin(match, 'ftn25')): 328 os.remove(pjoin(match, 'ftn25')) 329 330 #compile gensym 331 self.cmd.compile(['gensym'], cwd=Pdir) 332 if not os.path.exists(pjoin(Pdir, 'gensym')): 333 raise Exception('Error make gensym not successful') 334 335 # Launch gensym 336 p = misc.Popen(['./gensym'], stdout=subprocess.PIPE, 337 stderr=subprocess.STDOUT, cwd=Pdir) 338 #sym_input = "%(points)d %(iterations)d %(accuracy)f \n" % self.opts 339 (stdout, _) = p.communicate(''.encode()) 340 stdout = stdout.decode('ascii') 341 if os.path.exists(pjoin(self.me_dir,'error')): 342 files.mv(pjoin(self.me_dir,'error'), pjoin(Pdir,'ajob.no_ps.log')) 343 P_zero_result.append(subdir) 344 continue 345 346 jobs = stdout.split() 347 job_list[Pdir] = jobs 348 try: 349 # check that all input are valid 350 [float(s) for s in jobs] 351 except Exception: 352 logger.debug("unformated string found in gensym. Please check:\n %s" % stdout) 353 done=False 354 job_list[Pdir] = [] 355 lines = stdout.split('\n') 356 for l in lines: 357 try: 358 [float(s) for s in l.split()] 359 except: 360 continue 361 else: 362 if done: 363 raise Exception('Parsing error in gensym: %s' % stdout) 364 job_list[Pdir] = l.split() 365 done = True 366 if not done: 367 raise Exception('Parsing error in gensym: %s' % stdout) 368 369 self.cmd.compile(['madevent'], cwd=Pdir) 370 if to_submit: 371 self.submit_to_cluster(job_list) 372 job_list = {} 373 374 return job_list, P_zero_result
375
376 - def resubmit(self, min_precision=1.0, resubmit_zero=False):
377 """collect the result of the current run and relaunch each channel 378 not completed or optionally a completed one with a precision worse than 379 a threshold (and/or the zero result channel)""" 380 381 job_list, P_zero_result = self.launch(to_submit=False, clean=False) 382 383 for P , jobs in dict(job_list).items(): 384 misc.sprint(jobs) 385 to_resub = [] 386 for job in jobs: 387 if os.path.exists(pjoin(P, 'G%s' % job)) and os.path.exists(pjoin(P, 'G%s' % job, 'results.dat')): 388 one_result = sum_html.OneResult(job) 389 try: 390 one_result.read_results(pjoin(P, 'G%s' % job, 'results.dat')) 391 except: 392 to_resub.append(job) 393 if one_result.xsec == 0: 394 if resubmit_zero: 395 to_resub.append(job) 396 elif max(one_result.xerru, one_result.xerrc)/one_result.xsec > min_precision: 397 to_resub.append(job) 398 else: 399 to_resub.append(job) 400 if to_resub: 401 for G in to_resub: 402 try: 403 shutil.rmtree(pjoin(P, 'G%s' % G)) 404 except Exception as error: 405 misc.sprint(error) 406 pass 407 misc.sprint(to_resub) 408 self.submit_to_cluster({P: to_resub})
409 410 411 412 413 414 415 416 417 418 419
420 - def submit_to_cluster(self, job_list):
421 """ """ 422 423 if self.run_card['job_strategy'] > 0: 424 if len(job_list) >1: 425 for path, dirs in job_list.items(): 426 self.submit_to_cluster({path:dirs}) 427 return 428 path, value = list(job_list.items())[0] 429 nexternal = self.cmd.proc_characteristics['nexternal'] 430 current = open(pjoin(path, "nexternal.inc")).read() 431 ext = re.search(r"PARAMETER \(NEXTERNAL=(\d+)\)", current).group(1) 432 433 if self.run_card['job_strategy'] == 2: 434 self.splitted_grid = 2 435 if nexternal == int(ext): 436 to_split = 2 437 else: 438 to_split = 0 439 if hasattr(self, 'splitted_Pdir'): 440 self.splitted_Pdir[path] = to_split 441 else: 442 self.splitted_Pdir = {path: to_split} 443 self.splitted_for_dir = lambda x,y : self.splitted_Pdir[x] 444 elif self.run_card['job_strategy'] == 1: 445 if nexternal == int(ext): 446 combine = 1 447 else: 448 combine = self.combining_job 449 if hasattr(self, 'splitted_Pdir'): 450 self.splitted_Pdir[path] = combine 451 else: 452 self.splitted_Pdir = {path: combine} 453 self.combining_job_for_Pdir = lambda x : self.splitted_Pdir[x] 454 455 if not self.splitted_grid: 456 return self.submit_to_cluster_no_splitting(job_list) 457 elif self.cmd.cluster_mode == 0: 458 return self.submit_to_cluster_no_splitting(job_list) 459 elif self.cmd.cluster_mode == 2 and self.cmd.options['nb_core'] == 1: 460 return self.submit_to_cluster_no_splitting(job_list) 461 else: 462 return self.submit_to_cluster_splitted(job_list)
463 464
465 - def submit_to_cluster_no_splitting(self, job_list):
466 """submit the survey without the parralelization. 467 This is the old mode which is still usefull in single core""" 468 469 # write the template file for the parameter file 470 self.write_parameter(parralelization=False, Pdirs=list(job_list.keys())) 471 472 473 # launch the job with the appropriate grouping 474 for Pdir, jobs in job_list.items(): 475 jobs = list(jobs) 476 i=0 477 while jobs: 478 i+=1 479 to_submit = ['0'] # the first entry is actually the offset 480 for _ in range(self.combining_job_for_Pdir(Pdir)): 481 if jobs: 482 to_submit.append(jobs.pop(0)) 483 484 self.cmd.launch_job(pjoin(self.me_dir, 'SubProcesses', 'survey.sh'), 485 argument=to_submit, 486 cwd=pjoin(self.me_dir,'SubProcesses' , Pdir))
487 488
489 - def create_resubmit_one_iter(self, Pdir, G, submit_ps, nb_job, step=0):
490 """prepare the input_file for submitting the channel""" 491 492 493 if 'SubProcesses' not in Pdir: 494 Pdir = pjoin(self.me_dir, 'SubProcesses', Pdir) 495 496 #keep track of how many job are sended 497 self.splitted_Pdir[(Pdir, G)] = int(nb_job) 498 499 500 # 1. write the new input_app.txt 501 run_card = self.cmd.run_card 502 options = {'event' : submit_ps, 503 'maxiter': 1, 504 'miniter': 1, 505 'accuracy': self.cmd.opts['accuracy'], 506 'helicity': run_card['nhel_survey'] if 'nhel_survey' in run_card \ 507 else run_card['nhel'], 508 'gridmode': -2, 509 'channel' : G 510 } 511 512 Gdir = pjoin(Pdir, 'G%s' % G) 513 self.write_parameter_file(pjoin(Gdir, 'input_app.txt'), options) 514 515 # 2. check that ftn25 exists. 516 assert os.path.exists(pjoin(Gdir, "ftn25")) 517 518 519 # 3. Submit the new jobs 520 #call back function 521 packet = cluster.Packet((Pdir, G, step+1), 522 self.combine_iteration, 523 (Pdir, G, step+1)) 524 525 if step ==0: 526 self.lastoffset[(Pdir, G)] = 0 527 528 # resubmit the new jobs 529 for i in range(int(nb_job)): 530 name = "G%s_%s" % (G,i+1) 531 self.lastoffset[(Pdir, G)] += 1 532 offset = self.lastoffset[(Pdir, G)] 533 self.cmd.launch_job(pjoin(self.me_dir, 'SubProcesses', 'refine_splitted.sh'), 534 argument=[name, 'G%s'%G, offset], 535 cwd= Pdir, 536 packet_member=packet)
537 538
539 - def submit_to_cluster_splitted(self, job_list):
540 """ submit the version of the survey with splitted grid creation 541 """ 542 543 #if self.splitted_grid <= 1: 544 # return self.submit_to_cluster_no_splitting(job_list) 545 546 for Pdir, jobs in job_list.items(): 547 if not jobs: 548 continue 549 if self.splitted_for_dir(Pdir, jobs[0]) <= 1: 550 return self.submit_to_cluster_no_splitting({Pdir:jobs}) 551 552 self.write_parameter(parralelization=True, Pdirs=[Pdir]) 553 # launch the job with the appropriate grouping 554 555 for job in jobs: 556 packet = cluster.Packet((Pdir, job, 1), self.combine_iteration, (Pdir, job, 1)) 557 for i in range(self.splitted_for_dir(Pdir, job)): 558 self.cmd.launch_job(pjoin(self.me_dir, 'SubProcesses', 'survey.sh'), 559 argument=[i+1, job], 560 cwd=pjoin(self.me_dir,'SubProcesses' , Pdir), 561 packet_member=packet)
562
563 - def combine_iteration(self, Pdir, G, step):
564 565 grid_calculator, cross, error = self.combine_grid(Pdir, G, step) 566 567 # Compute the number of events used for this run. 568 nb_events = grid_calculator.target_evt 569 570 Gdirs = [] #build the the list of directory 571 for i in range(self.splitted_for_dir(Pdir, G)): 572 path = pjoin(Pdir, "G%s_%s" % (G, i+1)) 573 Gdirs.append(path) 574 575 # 4. make the submission of the next iteration 576 # Three cases - less than 3 iteration -> continue 577 # - more than 3 and less than 5 -> check error 578 # - more than 5 -> prepare info for refine 579 need_submit = False 580 if step < self.min_iterations and cross != 0: 581 if step == 1: 582 need_submit = True 583 else: 584 across = self.abscross[(Pdir,G)]/(self.sigma[(Pdir,G)]+1e-99) 585 tot_across = self.get_current_axsec() 586 if across / tot_across < 1e-6: 587 need_submit = False 588 elif error < self.cmd.opts['accuracy'] / 100: 589 need_submit = False 590 else: 591 need_submit = True 592 593 elif step >= self.cmd.opts['iterations']: 594 need_submit = False 595 elif self.cmd.opts['accuracy'] < 0: 596 #check for luminosity 597 raise Exception("Not Implemented") 598 elif self.abscross[(Pdir,G)] == 0: 599 need_submit = False 600 else: 601 across = self.abscross[(Pdir,G)]/(self.sigma[(Pdir,G)]+1e-99) 602 tot_across = self.get_current_axsec() 603 if across == 0: 604 need_submit = False 605 elif across / tot_across < 1e-5: 606 need_submit = False 607 elif error > self.cmd.opts['accuracy']: 608 need_submit = True 609 else: 610 need_submit = False 611 612 613 if cross: 614 grid_calculator.write_grid_for_submission(Pdir,G, 615 self.splitted_for_dir(Pdir, G), 616 nb_events,mode=self.mode, 617 conservative_factor=5.0) 618 619 xsec_format = '.%ig'%(max(3,int(math.log10(1.0/float(error)))+2) 620 if float(cross)!=0.0 and float(error)!=0.0 else 8) 621 if need_submit: 622 message = "%%s/G%%s is at %%%s +- %%.3g pb. Now submitting iteration #%s."%(xsec_format, step+1) 623 logger.info(message%\ 624 (os.path.basename(Pdir), G, float(cross), 625 float(error)*float(cross))) 626 self.resubmit_survey(Pdir,G, Gdirs, step) 627 elif cross: 628 logger.info("Survey finished for %s/G%s at %s"%( 629 os.path.basename(Pdir),G,('%%%s +- %%.3g pb'%xsec_format))% 630 (float(cross), float(error)*float(cross))) 631 # prepare information for refine 632 newGpath = pjoin(self.me_dir,'SubProcesses' , Pdir, 'G%s' % G) 633 if not os.path.exists(newGpath): 634 os.mkdir(newGpath) 635 636 # copy the new grid: 637 files.cp(pjoin(Gdirs[0], 'ftn25'), 638 pjoin(self.me_dir,'SubProcesses' , Pdir, 'G%s' % G, 'ftn26')) 639 640 # copy the events 641 fsock = open(pjoin(newGpath, 'events.lhe'), 'w') 642 for Gdir in Gdirs: 643 fsock.write(open(pjoin(Gdir, 'events.lhe')).read()) 644 645 # copy one log 646 files.cp(pjoin(Gdirs[0], 'log.txt'), 647 pjoin(self.me_dir,'SubProcesses' , Pdir, 'G%s' % G)) 648 649 650 # create the appropriate results.dat 651 self.write_results(grid_calculator, cross, error, Pdir, G, step) 652 else: 653 logger.info("Survey finished for %s/G%s [0 cross]", os.path.basename(Pdir),G) 654 655 Gdir = pjoin(self.me_dir,'SubProcesses' , Pdir, 'G%s' % G) 656 if not os.path.exists(Gdir): 657 os.mkdir(Gdir) 658 # copy one log 659 files.cp(pjoin(Gdirs[0], 'log.txt'), Gdir) 660 # create the appropriate results.dat 661 self.write_results(grid_calculator, cross, error, Pdir, G, step) 662 663 return 0
664
665 - def combine_grid(self, Pdir, G, step, exclude_sub_jobs=[]):
666 """ exclude_sub_jobs is to remove some of the subjobs if a numerical 667 issue is detected in one of them. Warning is issue when this occurs. 668 """ 669 670 # 1. create an object to combine the grid information and fill it 671 grid_calculator = combine_grid.grid_information(self.run_card['nhel']) 672 673 for i in range(self.splitted_for_dir(Pdir, G)): 674 if i in exclude_sub_jobs: 675 continue 676 path = pjoin(Pdir, "G%s_%s" % (G, i+1)) 677 fsock = misc.mult_try_open(pjoin(path, 'results.dat')) 678 one_result = grid_calculator.add_results_information(fsock) 679 fsock.close() 680 if one_result.axsec == 0: 681 grid_calculator.onefail = True 682 continue # grid_information might not exists 683 fsock = misc.mult_try_open(pjoin(path, 'grid_information')) 684 grid_calculator.add_one_grid_information(fsock) 685 fsock.close() 686 os.remove(pjoin(path, 'results.dat')) 687 #os.remove(pjoin(path, 'grid_information')) 688 689 690 691 #2. combine the information about the total crossection / error 692 # start by keep the interation in memory 693 cross, across, sigma = grid_calculator.get_cross_section() 694 695 #3. Try to avoid one single PS point which ruins the integration 696 # Should be related to loop evaluation instability. 697 maxwgt = grid_calculator.get_max_wgt(0.01) 698 if maxwgt: 699 nunwgt = grid_calculator.get_nunwgt(maxwgt) 700 # Make sure not to apply the security below during the first step of the 701 # survey. Also, disregard channels with a contribution relative to the 702 # total cross-section smaller than 1e-8 since in this case it is unlikely 703 # that this channel will need more than 1 event anyway. 704 apply_instability_security = False 705 rel_contrib = 0.0 706 if (self.__class__ != gensym or step > 1): 707 Pdir_across = 0.0 708 Gdir_across = 0.0 709 for (mPdir,mG) in self.abscross.keys(): 710 if mPdir == Pdir: 711 Pdir_across += (self.abscross[(mPdir,mG)]/ 712 (self.sigma[(mPdir,mG)]+1e-99)) 713 if mG == G: 714 Gdir_across += (self.abscross[(mPdir,mG)]/ 715 (self.sigma[(mPdir,mG)]+1e-99)) 716 rel_contrib = abs(Gdir_across/(Pdir_across+1e-99)) 717 if rel_contrib > (1.0e-8) and \ 718 nunwgt < 2 and len(grid_calculator.results) > 1: 719 apply_instability_security = True 720 721 if apply_instability_security: 722 # check the ratio between the different submit 723 th_maxwgt = [(r.th_maxwgt,i) for i,r in enumerate(grid_calculator.results)] 724 th_maxwgt.sort() 725 ratio = th_maxwgt[-1][0]/th_maxwgt[-2][0] 726 if ratio > 1e4: 727 logger.warning( 728 """"One Event with large weight have been found (ratio = %.3g) in channel G%s (with rel.contrib=%.3g). 729 This is likely due to numerical instabilities. The associated job is discarded to recover. 730 For offline investigation, the problematic discarded events are stored in: 731 %s"""%(ratio,G,rel_contrib,pjoin(Pdir,'DiscardedUnstableEvents'))) 732 exclude_sub_jobs = list(exclude_sub_jobs) 733 exclude_sub_jobs.append(th_maxwgt[-1][1]) 734 grid_calculator.results.run_statistics['skipped_subchannel'] += 1 735 736 # Add some monitoring of the problematic events 737 gPath = pjoin(Pdir, "G%s_%s" % (G, th_maxwgt[-1][1]+1)) 738 if os.path.isfile(pjoin(gPath,'events.lhe')): 739 lhe_file = lhe_parser.EventFile(pjoin(gPath,'events.lhe')) 740 discardedPath = pjoin(Pdir,'DiscardedUnstableEvents') 741 if not os.path.exists(discardedPath): 742 os.mkdir(discardedPath) 743 if os.path.isdir(discardedPath): 744 # Keep only the event with a maximum weight, as it surely 745 # is the problematic one. 746 evtRecord = open(pjoin(discardedPath,'discarded_G%s.dat'%G),'a') 747 lhe_file.seek(0) #rewind the file 748 try: 749 evtRecord.write('\n'+str(max(lhe_file,key=lambda evt:abs(evt.wgt)))) 750 except Exception: 751 #something wrong write the full file. 752 lhe_file.close() 753 evtRecord.write(pjoin(gPath,'events.lhe').read()) 754 evtRecord.close() 755 756 return self.combine_grid(Pdir, G, step, exclude_sub_jobs) 757 758 759 if across !=0: 760 if sigma != 0: 761 self.cross[(Pdir,G)] += cross**3/sigma**2 762 self.abscross[(Pdir,G)] += across * cross**2/sigma**2 763 self.sigma[(Pdir,G)] += cross**2/ sigma**2 764 self.chi2[(Pdir,G)] += cross**4/sigma**2 765 # and use those iteration to get the current estimator 766 cross = self.cross[(Pdir,G)]/self.sigma[(Pdir,G)] 767 if step > 1: 768 error = math.sqrt(abs((self.chi2[(Pdir,G)]/cross**2 - \ 769 self.sigma[(Pdir,G)])/(step-1))/self.sigma[(Pdir,G)]) 770 else: 771 error = sigma/cross 772 else: 773 self.cross[(Pdir,G)] = cross 774 self.abscross[(Pdir,G)] = across 775 self.sigma[(Pdir,G)] = 0 776 self.chi2[(Pdir,G)] = 0 777 cross = self.cross[(Pdir,G)] 778 error = 0 779 780 else: 781 error = 0 782 783 grid_calculator.results.compute_values(update_statistics=True) 784 if (str(os.path.basename(Pdir)), G) in self.run_statistics: 785 self.run_statistics[(str(os.path.basename(Pdir)), G)]\ 786 .aggregate_statistics(grid_calculator.results.run_statistics) 787 else: 788 self.run_statistics[(str(os.path.basename(Pdir)), G)] = \ 789 grid_calculator.results.run_statistics 790 791 self.warnings_from_statistics(G, grid_calculator.results.run_statistics) 792 stats_msg = grid_calculator.results.run_statistics.nice_output( 793 '/'.join([os.path.basename(Pdir),'G%s'%G])) 794 795 if stats_msg: 796 logger.log(5, stats_msg) 797 798 # Clean up grid_information to avoid border effects in case of a crash 799 for i in range(self.splitted_for_dir(Pdir, G)): 800 path = pjoin(Pdir, "G%s_%s" % (G, i+1)) 801 try: 802 os.remove(pjoin(path, 'grid_information')) 803 except OSError as oneerror: 804 if oneerror.errno != 2: 805 raise 806 return grid_calculator, cross, error
807
808 - def warnings_from_statistics(self,G,stats):
809 """Possible warn user for worrying MadLoop stats for this channel.""" 810 811 if stats['n_madloop_calls']==0: 812 return 813 814 EPS_fraction = float(stats['exceptional_points'])/stats['n_madloop_calls'] 815 816 msg = "Channel %s has encountered a fraction of %.3g\n"+ \ 817 "of numerically unstable loop matrix element computations\n"+\ 818 "(which could not be rescued using quadruple precision).\n"+\ 819 "The results might not be trusted." 820 821 if 0.01 > EPS_fraction > 0.001: 822 logger.warning(msg%(G,EPS_fraction)) 823 elif EPS_fraction > 0.01: 824 logger.critical((msg%(G,EPS_fraction)).replace('might', 'can')) 825 raise Exception((msg%(G,EPS_fraction)).replace('might', 'can'))
826
827 - def get_current_axsec(self):
828 829 across = 0 830 for (Pdir,G) in self.abscross: 831 across += self.abscross[(Pdir,G)]/(self.sigma[(Pdir,G)]+1e-99) 832 return across
833
834 - def write_results(self, grid_calculator, cross, error, Pdir, G, step):
835 836 #compute the value 837 if cross == 0: 838 abscross,nw, luminosity = 0, 0, 0 839 wgt, maxit,nunwgt, wgt, nevents = 0,0,0,0,0 840 maxwgt = 0 841 error = 0 842 else: 843 grid_calculator.results.compute_values() 844 abscross = self.abscross[(Pdir,G)]/self.sigma[(Pdir,G)] 845 nw = grid_calculator.results.nw 846 wgt = grid_calculator.results.wgt 847 maxit = step 848 wgt = 0 849 nevents = grid_calculator.results.nevents 850 maxwgt = grid_calculator.get_max_wgt() 851 nunwgt = grid_calculator.get_nunwgt() 852 luminosity = nunwgt/cross 853 854 #format the results.dat 855 def fstr(nb): 856 data = '%E' % nb 857 nb, power = data.split('E') 858 nb = float(nb) /10 859 power = int(power) + 1 860 return '%.5fE%+03i' %(nb,power)
861 line = '%s %s %s %i %i %i %i %s %s %s %s 0.0 0\n' % \ 862 (fstr(cross), fstr(error*cross), fstr(error*cross), 863 nevents, nw, maxit,nunwgt, 864 fstr(luminosity), fstr(wgt), fstr(abscross), fstr(maxwgt)) 865 866 fsock = open(pjoin(self.me_dir,'SubProcesses' , Pdir, 'G%s' % G, 867 'results.dat'),'w') 868 fsock.writelines(line) 869 fsock.close()
870
871 - def resubmit_survey(self, Pdir, G, Gdirs, step):
872 """submit the next iteration of the survey""" 873 874 # 1. write the new input_app.txt to double the number of points 875 run_card = self.cmd.run_card 876 options = {'event' : 2**(step) * self.cmd.opts['points'] / self.splitted_grid, 877 'maxiter': 1, 878 'miniter': 1, 879 'accuracy': self.cmd.opts['accuracy'], 880 'helicity': run_card['nhel_survey'] if 'nhel_survey' in run_card \ 881 else run_card['nhel'], 882 'gridmode': -2, 883 'channel' : '' 884 } 885 886 if int(options['helicity']) == 1: 887 options['event'] = options['event'] * 2**(self.cmd.proc_characteristics['nexternal']//3) 888 889 for Gdir in Gdirs: 890 self.write_parameter_file(pjoin(Gdir, 'input_app.txt'), options) 891 892 893 #2. resubmit the new jobs 894 packet = cluster.Packet((Pdir, G, step+1), self.combine_iteration, \ 895 (Pdir, G, step+1)) 896 nb_step = len(Gdirs) * (step+1) 897 for i,subdir in enumerate(Gdirs): 898 subdir = subdir.rsplit('_',1)[1] 899 subdir = int(subdir) 900 offset = nb_step+i+1 901 offset=str(offset) 902 tag = "%s.%s" % (subdir, offset) 903 904 self.cmd.launch_job(pjoin(self.me_dir, 'SubProcesses', 'survey.sh'), 905 argument=[tag, G], 906 cwd=pjoin(self.me_dir,'SubProcesses' , Pdir), 907 packet_member=packet)
908 909 910 911
912 - def write_parameter_file(self, path, options):
913 """ """ 914 915 template =""" %(event)s %(maxiter)s %(miniter)s !Number of events and max and min iterations 916 %(accuracy)s !Accuracy 917 %(gridmode)s !Grid Adjustment 0=none, 2=adjust 918 1 !Suppress Amplitude 1=yes 919 %(helicity)s !Helicity Sum/event 0=exact 920 %(channel)s """ 921 options['event'] = int(options['event']) 922 open(path, 'w').write(template % options)
923 924 925
926 - def write_parameter(self, parralelization, Pdirs=None):
927 """Write the parameter of the survey run""" 928 929 run_card = self.cmd.run_card 930 931 options = {'event' : self.cmd.opts['points'], 932 'maxiter': self.cmd.opts['iterations'], 933 'miniter': self.min_iterations, 934 'accuracy': self.cmd.opts['accuracy'], 935 'helicity': run_card['nhel_survey'] if 'nhel_survey' in run_card \ 936 else run_card['nhel'], 937 'gridmode': 2, 938 'channel': '' 939 } 940 941 if int(options['helicity'])== 1: 942 options['event'] = options['event'] * 2**(self.cmd.proc_characteristics['nexternal']//3) 943 944 if parralelization: 945 options['gridmode'] = -2 946 options['maxiter'] = 1 #this is automatic in dsample anyway 947 options['miniter'] = 1 #this is automatic in dsample anyway 948 options['event'] /= self.splitted_grid 949 950 if not Pdirs: 951 Pdirs = self.subproc 952 953 for Pdir in Pdirs: 954 path =pjoin(Pdir, 'input_app.txt') 955 self.write_parameter_file(path, options)
956
957 958 959 -class gen_ximprove(object):
960 961 962 # some hardcoded value which impact the generation 963 gen_events_security = 1.2 # multiply the number of requested event by this number for security 964 combining_job = 0 # allow to run multiple channel in sequence 965 max_request_event = 1000 # split jobs if a channel if it needs more than that 966 max_event_in_iter = 5000 967 min_event_in_iter = 1000 968 max_splitting = 130 # maximum duplication of a given channel 969 min_iter = 3 970 max_iter = 9 971 keep_grid_for_refine = False # only apply if needed to split the job 972 973 #convenient shortcut for the formatting of variable 974 @ staticmethod
975 - def format_variable(*args):
976 return bannermod.ConfigFile.format_variable(*args)
977 978
979 - def __new__(cls, cmd, opt):
980 """Choose in which type of refine we want to be""" 981 982 if hasattr(cls, 'force_class'): 983 if cls.force_class == 'gridpack': 984 return super(gen_ximprove, cls).__new__(gen_ximprove_gridpack) 985 elif cls.force_class == 'loop_induced': 986 return super(gen_ximprove, cls).__new__(gen_ximprove_share) 987 988 if cmd.proc_characteristics['loop_induced']: 989 return super(gen_ximprove, cls).__new__(gen_ximprove_share) 990 elif gen_ximprove.format_variable(cmd.run_card['gridpack'], bool): 991 return super(gen_ximprove, cls).__new__(gen_ximprove_gridpack) 992 elif cmd.run_card["job_strategy"] == 2: 993 return super(gen_ximprove, cls).__new__(gen_ximprove_share) 994 else: 995 return super(gen_ximprove, cls).__new__(gen_ximprove_v4)
996 997
998 - def __init__(self, cmd, opt=None):
999 1000 try: 1001 super(gen_ximprove, self).__init__(cmd, opt) 1002 except TypeError: 1003 pass 1004 1005 self.run_statistics = {} 1006 self.cmd = cmd 1007 self.run_card = cmd.run_card 1008 run_card = self.run_card 1009 self.me_dir = cmd.me_dir 1010 1011 #extract from the run_card the information that we need. 1012 self.gridpack = run_card['gridpack'] 1013 self.nhel = run_card['nhel'] 1014 if "nhel_refine" in run_card: 1015 self.nhel = run_card["nhel_refine"] 1016 1017 if self.run_card['refine_evt_by_job'] != -1: 1018 self.max_request_event = run_card['refine_evt_by_job'] 1019 1020 1021 # Default option for the run 1022 self.gen_events = True 1023 self.parralel = False 1024 # parameter which was input for the normal gen_ximprove run 1025 self.err_goal = 0.01 1026 self.max_np = 9 1027 self.split_channels = False 1028 # parameter for the gridpack run 1029 self.nreq = 2000 1030 self.iseed = 4321 1031 1032 # placeholder for information 1033 self.results = 0 #updated in launch/update_html 1034 1035 if isinstance(opt, dict): 1036 self.configure(opt) 1037 elif isinstance(opt, bannermod.GridpackCard): 1038 self.configure_gridpack(opt)
1039
1040 - def __call__(self):
1041 return self.launch()
1042
1043 - def launch(self):
1044 """running """ 1045 1046 #start the run 1047 self.handle_seed() 1048 self.results = sum_html.collect_result(self.cmd, 1049 main_dir=pjoin(self.cmd.me_dir,'SubProcesses')) #main_dir is for gridpack readonly mode 1050 if self.gen_events: 1051 # We run to provide a given number of events 1052 self.get_job_for_event() 1053 else: 1054 # We run to achieve a given precision 1055 self.get_job_for_precision()
1056 1057
1058 - def configure(self, opt):
1059 """Defines some parameter of the run""" 1060 1061 for key, value in opt.items(): 1062 if key in self.__dict__: 1063 targettype = type(getattr(self, key)) 1064 setattr(self, key, self.format_variable(value, targettype, key)) 1065 else: 1066 raise Exception('%s not define' % key) 1067 1068 1069 # special treatment always do outside the loop to avoid side effect 1070 if 'err_goal' in opt: 1071 if self.err_goal < 1: 1072 logger.info("running for accuracy %s%%" % (self.err_goal*100)) 1073 self.gen_events = False 1074 elif self.err_goal >= 1: 1075 logger.info("Generating %s unweighted events." % self.err_goal) 1076 self.gen_events = True 1077 self.err_goal = self.err_goal * self.gen_events_security # security
1078
1079 - def handle_seed(self):
1080 """not needed but for gridpack --which is not handle here for the moment""" 1081 return
1082 1083
1084 - def find_job_for_event(self):
1085 """return the list of channel that need to be improved""" 1086 1087 assert self.err_goal >=1 1088 self.err_goal = int(self.err_goal) 1089 1090 goal_lum = self.err_goal/(self.results.axsec+1e-99) #pb^-1 1091 logger.info('Effective Luminosity %s pb^-1', goal_lum) 1092 1093 all_channels = sum([list(P) for P in self.results],[]) 1094 all_channels.sort(key= lambda x:x.get('luminosity'), reverse=True) 1095 1096 to_refine = [] 1097 for C in all_channels: 1098 if C.get('axsec') == 0: 1099 continue 1100 if goal_lum/(C.get('luminosity')+1e-99) >= 1 + (self.gen_events_security-1)/2: 1101 logger.debug("channel %s need to improve by %.2f (xsec=%s pb, iter=%s)", C.name, goal_lum/(C.get('luminosity')+1e-99), C.get('xsec'), int(C.get('maxit'))) 1102 to_refine.append(C) 1103 elif C.get('xerr') > max(C.get('axsec'), 1104 (1/(100*math.sqrt(self.err_goal)))*all_channels[-1].get('axsec')): 1105 to_refine.append(C) 1106 1107 logger.info('need to improve %s channels' % len(to_refine)) 1108 return goal_lum, to_refine
1109
1110 - def update_html(self):
1111 """update the html from this object since it contains all the information""" 1112 1113 1114 run = self.cmd.results.current['run_name'] 1115 if not os.path.exists(pjoin(self.cmd.me_dir, 'HTML', run)): 1116 os.mkdir(pjoin(self.cmd.me_dir, 'HTML', run)) 1117 1118 unit = self.cmd.results.unit 1119 P_text = "" 1120 if self.results: 1121 Presults = self.results 1122 else: 1123 self.results = sum_html.collect_result(self.cmd, None) 1124 Presults = self.results 1125 1126 for P_comb in Presults: 1127 P_text += P_comb.get_html(run, unit, self.cmd.me_dir) 1128 1129 Presults.write_results_dat(pjoin(self.cmd.me_dir,'SubProcesses', 'results.dat')) 1130 1131 fsock = open(pjoin(self.cmd.me_dir, 'HTML', run, 'results.html'),'w') 1132 fsock.write(sum_html.results_header) 1133 fsock.write('%s <dl>' % Presults.get_html(run, unit, self.cmd.me_dir)) 1134 fsock.write('%s </dl></body>' % P_text) 1135 1136 self.cmd.results.add_detail('cross', Presults.xsec) 1137 self.cmd.results.add_detail('error', Presults.xerru) 1138 1139 return Presults.xsec, Presults.xerru
1140
1141 1142 -class gen_ximprove_v4(gen_ximprove):
1143 1144 # some hardcoded value which impact the generation 1145 gen_events_security = 1.2 # multiply the number of requested event by this number for security 1146 combining_job = 0 # allow to run multiple channel in sequence 1147 max_request_event = 1000 # split jobs if a channel if it needs more than that 1148 max_event_in_iter = 5000 1149 min_event_in_iter = 1000 1150 max_splitting = 130 # maximum duplication of a given channel 1151 min_iter = 3 1152 max_iter = 9 1153 keep_grid_for_refine = False # only apply if needed to split the job 1154 1155 1156
1157 - def __init__(self, cmd, opt=None):
1158 1159 super(gen_ximprove_v4, self).__init__(cmd, opt) 1160 1161 if cmd.opts['accuracy'] < cmd._survey_options['accuracy'][1]: 1162 self.increase_precision(cmd._survey_options['accuracy'][1]/cmd.opts['accuracy'])
1163
1164 - def reset_multijob(self):
1165 1166 for path in misc.glob(pjoin('*', '*','multijob.dat'), pjoin(self.me_dir, 'SubProcesses')): 1167 open(path,'w').write('0\n')
1168
1169 - def write_multijob(self, Channel, nb_split):
1170 """ """ 1171 if nb_split <=1: 1172 return 1173 f = open(pjoin(self.me_dir, 'SubProcesses', Channel.get('name'), 'multijob.dat'), 'w') 1174 f.write('%i\n' % nb_split) 1175 f.close()
1176
1177 - def increase_precision(self, rate=3):
1178 #misc.sprint(rate) 1179 if rate < 3: 1180 self.max_event_in_iter = 20000 1181 self.min_events = 7500 1182 self.gen_events_security = 1.3 1183 else: 1184 rate = rate -2 1185 self.max_event_in_iter = int((rate+1) * 10000) 1186 self.min_events = int(rate+2) * 2500 1187 self.gen_events_security = 1 + 0.1 * (rate+2) 1188 1189 if int(self.nhel) == 1: 1190 self.min_event_in_iter *= 2**(self.cmd.proc_characteristics['nexternal']//3) 1191 self.max_event_in_iter *= 2**(self.cmd.proc_characteristics['nexternal']//2)
1192 1193 1194 1195 alphabet = "abcdefghijklmnopqrstuvwxyz"
1196 - def get_job_for_event(self):
1197 """generate the script in order to generate a given number of event""" 1198 # correspond to write_gen in the fortran version 1199 1200 1201 goal_lum, to_refine = self.find_job_for_event() 1202 1203 #reset the potential multijob of previous run 1204 self.reset_multijob() 1205 1206 jobs = [] # list of the refine if some job are split is list of 1207 # dict with the parameter of the run. 1208 1209 # try to have a smart load on the cluster (not really important actually) 1210 if self.combining_job >1: 1211 # add a nice ordering for the jobs 1212 new_order = [] 1213 if self.combining_job % 2 == 0: 1214 for i in range(len(to_refine) //2): 1215 new_order.append(to_refine[i]) 1216 new_order.append(to_refine[-i-1]) 1217 if len(to_refine) % 2: 1218 new_order.append(to_refine[i+1]) 1219 else: 1220 for i in range(len(to_refine) //3): 1221 new_order.append(to_refine[i]) 1222 new_order.append(to_refine[-2*i-1]) 1223 new_order.append(to_refine[-2*i-2]) 1224 if len(to_refine) % 3 == 1: 1225 new_order.append(to_refine[i+1]) 1226 elif len(to_refine) % 3 == 2: 1227 new_order.append(to_refine[i+2]) 1228 #ensure that the reordering is done nicely 1229 assert set([id(C) for C in to_refine]) == set([id(C) for C in new_order]) 1230 to_refine = new_order 1231 1232 1233 # loop over the channel to refine 1234 for C in to_refine: 1235 #1. Compute the number of points are needed to reach target 1236 needed_event = goal_lum*C.get('axsec') 1237 nb_split = int(max(1,((needed_event-1)// self.max_request_event) +1)) 1238 if not self.split_channels: 1239 nb_split = 1 1240 if nb_split > self.max_splitting: 1241 nb_split = self.max_splitting 1242 nb_split=max(1, nb_split) 1243 1244 1245 #2. estimate how many points we need in each iteration 1246 if C.get('nunwgt') > 0: 1247 nevents = needed_event / nb_split * (C.get('nevents') / C.get('nunwgt')) 1248 #split by iter 1249 nevents = int(nevents / (2**self.min_iter-1)) 1250 else: 1251 nevents = self.max_event_in_iter 1252 1253 if nevents < self.min_event_in_iter: 1254 nb_split = int(nb_split * nevents / self.min_event_in_iter) + 1 1255 nevents = self.min_event_in_iter 1256 # 1257 # forbid too low/too large value 1258 nevents = max(self.min_event_in_iter, min(self.max_event_in_iter, nevents)) 1259 logger.debug("%s : need %s event. Need %s split job of %s points", C.name, needed_event, nb_split, nevents) 1260 1261 1262 # write the multi-job information 1263 self.write_multijob(C, nb_split) 1264 1265 packet = cluster.Packet((C.parent_name, C.name), 1266 combine_runs.CombineRuns, 1267 (pjoin(self.me_dir, 'SubProcesses', C.parent_name)), 1268 {"subproc": C.name, "nb_split":nb_split}) 1269 1270 1271 #create the info dict assume no splitting for the default 1272 info = {'name': self.cmd.results.current['run_name'], 1273 'script_name': 'unknown', 1274 'directory': C.name, # need to be change for splitted job 1275 'P_dir': C.parent_name, 1276 'Ppath': pjoin(self.cmd.me_dir, 'SubProcesses', C.parent_name), 1277 'offset': 1, # need to be change for splitted job 1278 'nevents': nevents, 1279 'maxiter': self.max_iter, 1280 'miniter': self.min_iter, 1281 'precision': -goal_lum/nb_split, 1282 'nhel': self.run_card['nhel'], 1283 'channel': C.name.replace('G',''), 1284 'grid_refinment' : 0, #no refinment of the grid 1285 'base_directory': '', #should be change in splitted job if want to keep the grid 1286 'packet': packet, 1287 } 1288 1289 if nb_split == 1: 1290 jobs.append(info) 1291 else: 1292 for i in range(nb_split): 1293 new_info = dict(info) 1294 new_info['offset'] = i+1 1295 new_info['directory'] += self.alphabet[i % 26] + str((i+1)//26) 1296 if self.keep_grid_for_refine: 1297 new_info['base_directory'] = info['directory'] 1298 jobs.append(new_info) 1299 1300 self.create_ajob(pjoin(self.me_dir, 'SubProcesses', 'refine.sh'), jobs)
1301 1302
1303 - def create_ajob(self, template, jobs, write_dir=None):
1304 """create the ajob""" 1305 1306 if not jobs: 1307 return 1308 1309 if not write_dir: 1310 write_dir = pjoin(self.me_dir, 'SubProcesses') 1311 1312 #filter the job according to their SubProcess directory # no mix submition 1313 P2job= collections.defaultdict(list) 1314 for j in jobs: 1315 P2job[j['P_dir']].append(j) 1316 if len(P2job) >1: 1317 for P in P2job.values(): 1318 self.create_ajob(template, P, write_dir) 1319 return 1320 1321 1322 #Here we can assume that all job are for the same directory. 1323 path = pjoin(write_dir, jobs[0]['P_dir']) 1324 1325 template_text = open(template, 'r').read() 1326 # special treatment if needed to combine the script 1327 # computes how many submition miss one job 1328 if self.combining_job > 1: 1329 skip1=0 1330 n_channels = len(jobs) 1331 nb_sub = n_channels // self.combining_job 1332 nb_job_in_last = n_channels % self.combining_job 1333 if nb_sub == 0: 1334 nb_sub = 1 1335 nb_job_in_last =0 1336 if nb_job_in_last: 1337 nb_sub +=1 1338 skip1 = self.combining_job - nb_job_in_last 1339 if skip1 > nb_sub: 1340 self.combining_job -=1 1341 return self.create_ajob(template, jobs, write_dir) 1342 combining_job = self.combining_job 1343 else: 1344 #define the variable for combining jobs even in not combine mode 1345 #such that we can use the same routine 1346 skip1=0 1347 combining_job =1 1348 nb_sub = len(jobs) 1349 1350 1351 nb_use = 0 1352 for i in range(nb_sub): 1353 script_number = i+1 1354 if i < skip1: 1355 nb_job = combining_job -1 1356 else: 1357 nb_job = min(combining_job, len(jobs)) 1358 fsock = open(pjoin(path, 'ajob%i' % script_number), 'w') 1359 for j in range(nb_use, nb_use + nb_job): 1360 if j> len(jobs): 1361 break 1362 info = jobs[j] 1363 info['script_name'] = 'ajob%i' % script_number 1364 info['keeplog'] = 'false' 1365 if "base_directory" not in info: 1366 info["base_directory"] = "./" 1367 fsock.write(template_text % info) 1368 nb_use += nb_job 1369 1370 fsock.close() 1371 return script_number
1372
1373 - def get_job_for_precision(self):
1374 """create the ajob to achieve a give precision on the total cross-section""" 1375 1376 1377 assert self.err_goal <=1 1378 xtot = abs(self.results.xsec) 1379 logger.info("Working on precision: %s %%" %(100*self.err_goal)) 1380 all_channels = sum([list(P) for P in self.results if P.mfactor],[]) 1381 limit = self.err_goal * xtot / len(all_channels) 1382 to_refine = [] 1383 rerr = 0 #error of the job not directly selected 1384 for C in all_channels: 1385 cerr = C.mfactor*(C.xerru + len(all_channels)*C.xerrc) 1386 if cerr > abs(limit): 1387 to_refine.append(C) 1388 else: 1389 rerr += cerr 1390 rerr *=rerr 1391 if not len(to_refine): 1392 return 1393 1394 # change limit since most don't contribute 1395 limit = math.sqrt((self.err_goal * xtot)**2 - rerr/math.sqrt(len(to_refine))) 1396 for C in to_refine[:]: 1397 cerr = C.mfactor*(C.xerru + len(to_refine)*C.xerrc) 1398 if cerr < limit: 1399 to_refine.remove(C) 1400 1401 # all the channel are now selected. create the channel information 1402 logger.info('need to improve %s channels' % len(to_refine)) 1403 1404 1405 jobs = [] # list of the refine if some job are split is list of 1406 # dict with the parameter of the run. 1407 1408 # loop over the channel to refine 1409 for C in to_refine: 1410 1411 #1. Determine how many events we need in each iteration 1412 yerr = C.mfactor*(C.xerru+len(to_refine)*C.xerrc) 1413 nevents = 0.2*C.nevents*(yerr/limit)**2 1414 1415 nb_split = int((nevents*(C.nunwgt/C.nevents)/self.max_request_event/ (2**self.min_iter-1))**(2/3)) 1416 nb_split = max(nb_split, 1) 1417 # **(2/3) to slow down the increase in number of jobs 1418 if nb_split > self.max_splitting: 1419 nb_split = self.max_splitting 1420 1421 if nb_split >1: 1422 nevents = nevents / nb_split 1423 self.write_multijob(C, nb_split) 1424 # forbid too low/too large value 1425 nevents = min(self.min_event_in_iter, max(self.max_event_in_iter, nevents)) 1426 1427 1428 #create the info dict assume no splitting for the default 1429 info = {'name': self.cmd.results.current['run_name'], 1430 'script_name': 'unknown', 1431 'directory': C.name, # need to be change for splitted job 1432 'P_dir': C.parent_name, 1433 'Ppath': pjoin(self.cmd.me_dir, 'SubProcesses', C.parent_name), 1434 'offset': 1, # need to be change for splitted job 1435 'nevents': nevents, 1436 'maxiter': self.max_iter, 1437 'miniter': self.min_iter, 1438 'precision': yerr/math.sqrt(nb_split)/(C.get('xsec')+ yerr), 1439 'nhel': self.run_card['nhel'], 1440 'channel': C.name.replace('G',''), 1441 'grid_refinment' : 1 1442 } 1443 1444 if nb_split == 1: 1445 jobs.append(info) 1446 else: 1447 for i in range(nb_split): 1448 new_info = dict(info) 1449 new_info['offset'] = i+1 1450 new_info['directory'] += self.alphabet[i % 26] + str((i+1)//26) 1451 jobs.append(new_info) 1452 self.create_ajob(pjoin(self.me_dir, 'SubProcesses', 'refine.sh'), jobs)
1453
1454 - def update_html(self):
1455 """update the html from this object since it contains all the information""" 1456 1457 1458 run = self.cmd.results.current['run_name'] 1459 if not os.path.exists(pjoin(self.cmd.me_dir, 'HTML', run)): 1460 os.mkdir(pjoin(self.cmd.me_dir, 'HTML', run)) 1461 1462 unit = self.cmd.results.unit 1463 P_text = "" 1464 if self.results: 1465 Presults = self.results 1466 else: 1467 self.results = sum_html.collect_result(self.cmd, None) 1468 Presults = self.results 1469 1470 for P_comb in Presults: 1471 P_text += P_comb.get_html(run, unit, self.cmd.me_dir) 1472 1473 Presults.write_results_dat(pjoin(self.cmd.me_dir,'SubProcesses', 'results.dat')) 1474 1475 fsock = open(pjoin(self.cmd.me_dir, 'HTML', run, 'results.html'),'w') 1476 fsock.write(sum_html.results_header) 1477 fsock.write('%s <dl>' % Presults.get_html(run, unit, self.cmd.me_dir)) 1478 fsock.write('%s </dl></body>' % P_text) 1479 1480 self.cmd.results.add_detail('cross', Presults.xsec) 1481 self.cmd.results.add_detail('error', Presults.xerru) 1482 1483 return Presults.xsec, Presults.xerru
1484
1485 1486 1487 1488 -class gen_ximprove_v4_nogridupdate(gen_ximprove_v4):
1489 1490 # some hardcoded value which impact the generation 1491 gen_events_security = 1.1 # multiply the number of requested event by this number for security 1492 combining_job = 0 # allow to run multiple channel in sequence 1493 max_request_event = 400 # split jobs if a channel if it needs more than that 1494 max_event_in_iter = 500 1495 min_event_in_iter = 250 1496 max_splitting = 260 # maximum duplication of a given channel 1497 min_iter = 2 1498 max_iter = 6 1499 keep_grid_for_refine = True 1500 1501
1502 - def __init__(self, cmd, opt=None):
1503 1504 gen_ximprove.__init__(cmd, opt) 1505 1506 if cmd.proc_characteristics['loopinduced'] and \ 1507 cmd.proc_characteristics['nexternal'] > 2: 1508 self.increase_parralelization(cmd.proc_characteristics['nexternal'])
1509
1510 - def increase_parralelization(self, nexternal):
1511 1512 self.max_splitting = 1000 1513 1514 if self.run_card['refine_evt_by_job'] != -1: 1515 pass 1516 elif nexternal == 3: 1517 self.max_request_event = 200 1518 elif nexternal == 4: 1519 self.max_request_event = 100 1520 elif nexternal >= 5: 1521 self.max_request_event = 50 1522 self.min_event_in_iter = 125 1523 self.max_iter = 5
1524
1525 -class gen_ximprove_share(gen_ximprove, gensym):
1526 """Doing the refine in multicore. Each core handle a couple of PS point.""" 1527 1528 nb_ps_by_job = 2000 1529 mode = "refine" 1530 gen_events_security = 1.15 1531 # Note the real security is lower since we stop the jobs if they are at 96% 1532 # of this target. 1533
1534 - def __init__(self, *args, **opts):
1535 1536 super(gen_ximprove_share, self).__init__(*args, **opts) 1537 self.generated_events = {} 1538 self.splitted_for_dir = lambda x,y : self.splitted_Pdir[(x,y)]
1539 1540
1541 - def get_job_for_event(self):
1542 """generate the script in order to generate a given number of event""" 1543 # correspond to write_gen in the fortran version 1544 1545 1546 goal_lum, to_refine = self.find_job_for_event() 1547 self.goal_lum = goal_lum 1548 1549 # loop over the channel to refine to find the number of PS point to launch 1550 total_ps_points = 0 1551 channel_to_ps_point = [] 1552 for C in to_refine: 1553 #0. remove previous events files 1554 try: 1555 os.remove(pjoin(self.me_dir, "SubProcesses",C.parent_name, C.name, "events.lhe")) 1556 except: 1557 pass 1558 1559 #1. Compute the number of points are needed to reach target 1560 needed_event = goal_lum*C.get('axsec') 1561 if needed_event == 0: 1562 continue 1563 #2. estimate how many points we need in each iteration 1564 if C.get('nunwgt') > 0: 1565 nevents = needed_event * (C.get('nevents') / C.get('nunwgt')) 1566 #split by iter 1567 nevents = int(nevents / (2**self.min_iter-1)) 1568 else: 1569 nb_split = int(max(1,((needed_event-1)// self.max_request_event) +1)) 1570 if not self.split_channels: 1571 nb_split = 1 1572 if nb_split > self.max_splitting: 1573 nb_split = self.max_splitting 1574 nevents = self.max_event_in_iter * self.max_splitting 1575 else: 1576 nevents = self.max_event_in_iter * nb_split 1577 1578 if nevents > self.max_splitting*self.max_event_in_iter: 1579 logger.warning("Channel %s/%s has a very low efficiency of unweighting. Might not be possible to reach target" % \ 1580 (C.name, C.parent_name)) 1581 nevents = self.max_event_in_iter * self.max_splitting 1582 1583 total_ps_points += nevents 1584 channel_to_ps_point.append((C, nevents)) 1585 1586 if self.cmd.options["run_mode"] == 1: 1587 if self.cmd.options["cluster_size"]: 1588 nb_ps_by_job = total_ps_points /int(self.cmd.options["cluster_size"]) 1589 else: 1590 nb_ps_by_job = self.nb_ps_by_job 1591 elif self.cmd.options["run_mode"] == 2: 1592 remain = total_ps_points % self.cmd.options["nb_core"] 1593 if remain: 1594 nb_ps_by_job = 1 + (total_ps_points - remain) / self.cmd.options["nb_core"] 1595 else: 1596 nb_ps_by_job = total_ps_points / self.cmd.options["nb_core"] 1597 else: 1598 nb_ps_by_job = self.nb_ps_by_job 1599 1600 nb_ps_by_job = int(max(nb_ps_by_job, 500)) 1601 1602 for C, nevents in channel_to_ps_point: 1603 if nevents % nb_ps_by_job: 1604 nb_job = 1 + int(nevents // nb_ps_by_job) 1605 else: 1606 nb_job = int(nevents // nb_ps_by_job) 1607 submit_ps = min(nevents, nb_ps_by_job) 1608 if nb_job == 1: 1609 submit_ps = max(submit_ps, self.min_event_in_iter) 1610 self.create_resubmit_one_iter(C.parent_name, C.name[1:], submit_ps, nb_job, step=0) 1611 needed_event = goal_lum*C.get('xsec') 1612 logger.debug("%s/%s : need %s event. Need %s split job of %s points", C.parent_name, C.name, needed_event, nb_job, submit_ps)
1613 1614
1615 - def combine_iteration(self, Pdir, G, step):
1616 1617 grid_calculator, cross, error = self.combine_grid(Pdir, G, step) 1618 1619 # collect all the generated_event 1620 Gdirs = [] #build the the list of directory 1621 for i in range(self.splitted_for_dir(Pdir, G)): 1622 path = pjoin(Pdir, "G%s_%s" % (G, i+1)) 1623 Gdirs.append(path) 1624 assert len(grid_calculator.results) == len(Gdirs) == self.splitted_for_dir(Pdir, G) 1625 1626 1627 # Check how many events are going to be kept after un-weighting. 1628 needed_event = cross * self.goal_lum 1629 if needed_event == 0: 1630 return 0 1631 # check that the number of events requested is not higher than the actual 1632 # total number of events to generate. 1633 if self.err_goal >=1: 1634 if needed_event > self.gen_events_security * self.err_goal: 1635 needed_event = int(self.gen_events_security * self.err_goal) 1636 1637 if (Pdir, G) in self.generated_events: 1638 old_nunwgt, old_maxwgt = self.generated_events[(Pdir, G)] 1639 else: 1640 old_nunwgt, old_maxwgt = 0, 0 1641 1642 if old_nunwgt == 0 and os.path.exists(pjoin(Pdir,"G%s" % G, "events.lhe")): 1643 # possible for second refine. 1644 lhe = lhe_parser.EventFile(pjoin(Pdir,"G%s" % G, "events.lhe")) 1645 old_nunwgt = lhe.unweight(None, trunc_error=0.005, log_level=0) 1646 old_maxwgt = lhe.max_wgt 1647 1648 1649 1650 maxwgt = max(grid_calculator.get_max_wgt(), old_maxwgt) 1651 new_evt = grid_calculator.get_nunwgt(maxwgt) 1652 efficiency = new_evt / sum([R.nevents for R in grid_calculator.results]) 1653 nunwgt = old_nunwgt * old_maxwgt / maxwgt 1654 nunwgt += new_evt 1655 1656 # check the number of event for this iteration alone 1657 one_iter_nb_event = max(grid_calculator.get_nunwgt(),1) 1658 drop_previous_iteration = False 1659 # compare the number of events to generate if we discard the previous iteration 1660 n_target_one_iter = (needed_event-one_iter_nb_event) / ( one_iter_nb_event/ sum([R.nevents for R in grid_calculator.results])) 1661 n_target_combined = (needed_event-nunwgt) / efficiency 1662 if n_target_one_iter < n_target_combined: 1663 # the last iteration alone has more event that the combine iteration. 1664 # it is therefore interesting to drop previous iteration. 1665 drop_previous_iteration = True 1666 nunwgt = one_iter_nb_event 1667 maxwgt = grid_calculator.get_max_wgt() 1668 new_evt = nunwgt 1669 efficiency = ( one_iter_nb_event/ sum([R.nevents for R in grid_calculator.results])) 1670 1671 try: 1672 if drop_previous_iteration: 1673 raise IOError 1674 output_file = open(pjoin(Pdir,"G%s" % G, "events.lhe"), 'a') 1675 except IOError: 1676 output_file = open(pjoin(Pdir,"G%s" % G, "events.lhe"), 'w') 1677 1678 misc.call(["cat"] + [pjoin(d, "events.lhe") for d in Gdirs], 1679 stdout=output_file) 1680 output_file.close() 1681 # For large number of iteration. check the number of event by doing the 1682 # real unweighting. 1683 if nunwgt < 0.6 * needed_event and step > self.min_iter: 1684 lhe = lhe_parser.EventFile(output_file.name) 1685 old_nunwgt =nunwgt 1686 nunwgt = lhe.unweight(None, trunc_error=0.01, log_level=0) 1687 1688 1689 self.generated_events[(Pdir, G)] = (nunwgt, maxwgt) 1690 1691 # misc.sprint("Adding %s event to %s. Currently at %s" % (new_evt, G, nunwgt)) 1692 # check what to do 1693 if nunwgt >= int(0.96*needed_event)+1: # 0.96*1.15=1.10 =real security 1694 # We did it. 1695 logger.info("found enough event for %s/G%s" % (os.path.basename(Pdir), G)) 1696 self.write_results(grid_calculator, cross, error, Pdir, G, step, efficiency) 1697 return 0 1698 elif step >= self.max_iter: 1699 logger.debug("fail to find enough event") 1700 self.write_results(grid_calculator, cross, error, Pdir, G, step, efficiency) 1701 return 0 1702 1703 nb_split_before = len(grid_calculator.results) 1704 nevents = grid_calculator.results[0].nevents 1705 if nevents == 0: # possible if some integral returns 0 1706 nevents = max(g.nevents for g in grid_calculator.results) 1707 1708 need_ps_point = (needed_event - nunwgt)/(efficiency+1e-99) 1709 need_job = need_ps_point // nevents + 1 1710 1711 if step < self.min_iter: 1712 # This is normal but check if we are on the good track 1713 job_at_first_iter = nb_split_before/2**(step-1) 1714 expected_total_job = job_at_first_iter * (2**self.min_iter-1) 1715 done_job = job_at_first_iter * (2**step-1) 1716 expected_remaining_job = expected_total_job - done_job 1717 1718 logger.debug("efficiency status (smaller is better): %s", need_job/expected_remaining_job) 1719 # increase if needed but not too much 1720 need_job = min(need_job, expected_remaining_job*1.25) 1721 1722 nb_job = (need_job-0.5)//(2**(self.min_iter-step)-1) + 1 1723 nb_job = max(1, nb_job) 1724 grid_calculator.write_grid_for_submission(Pdir,G, 1725 self.splitted_for_dir(Pdir, G), nb_job*nevents ,mode=self.mode, 1726 conservative_factor=self.max_iter) 1727 logger.info("%s/G%s is at %i/%i (%.2g%%) event. Resubmit %i job at iteration %i." \ 1728 % (os.path.basename(Pdir), G, int(nunwgt),int(needed_event)+1, 1729 (float(nunwgt)/needed_event)*100.0 if needed_event>0.0 else 0.0, 1730 nb_job, step)) 1731 self.create_resubmit_one_iter(Pdir, G, nevents, nb_job, step) 1732 #self.create_job(Pdir, G, nb_job, nevents, step) 1733 1734 elif step < self.max_iter: 1735 if step + 1 == self.max_iter: 1736 need_job = 1.20 * need_job # avoid to have just too few event. 1737 1738 nb_job = int(min(need_job, nb_split_before*1.5)) 1739 grid_calculator.write_grid_for_submission(Pdir,G, 1740 self.splitted_for_dir(Pdir, G), nb_job*nevents ,mode=self.mode, 1741 conservative_factor=self.max_iter) 1742 1743 1744 logger.info("%s/G%s is at %i/%i ('%.2g%%') event. Resubmit %i job at iteration %i." \ 1745 % (os.path.basename(Pdir), G, int(nunwgt),int(needed_event)+1, 1746 (float(nunwgt)/needed_event)*100.0 if needed_event>0.0 else 0.0, 1747 nb_job, step)) 1748 self.create_resubmit_one_iter(Pdir, G, nevents, nb_job, step) 1749 1750 1751 1752 return 0
1753 1754
1755 - def write_results(self, grid_calculator, cross, error, Pdir, G, step, efficiency):
1756 1757 #compute the value 1758 if cross == 0: 1759 abscross,nw, luminosity = 0, 0, 0 1760 wgt, maxit,nunwgt, wgt, nevents = 0,0,0,0,0 1761 error = 0 1762 else: 1763 grid_calculator.results.compute_values() 1764 abscross = self.abscross[(Pdir,G)]/self.sigma[(Pdir,G)] 1765 nunwgt, wgt = self.generated_events[(Pdir, G)] 1766 nw = int(nunwgt / efficiency) 1767 nunwgt = int(nunwgt) 1768 maxit = step 1769 nevents = nunwgt 1770 # make the unweighting to compute the number of events: 1771 luminosity = nunwgt/cross 1772 1773 #format the results.dat 1774 def fstr(nb): 1775 data = '%E' % nb 1776 nb, power = data.split('E') 1777 nb = float(nb) /10 1778 power = int(power) + 1 1779 return '%.5fE%+03i' %(nb,power)
1780 line = '%s %s %s %i %i %i %i %s %s %s 0.0 0.0 0\n' % \ 1781 (fstr(cross), fstr(error*cross), fstr(error*cross), 1782 nevents, nw, maxit,nunwgt, 1783 fstr(luminosity), fstr(wgt), fstr(abscross)) 1784 1785 fsock = open(pjoin(self.me_dir,'SubProcesses' , Pdir, 'G%s' % G, 1786 'results.dat'),'w') 1787 fsock.writelines(line) 1788 fsock.close()
1789
1790 1791 1792 1793 -class gen_ximprove_gridpack(gen_ximprove_v4):
1794 1795 min_iter = 1 1796 max_iter = 13 1797 max_request_event = 1e12 # split jobs if a channel if it needs more than that 1798 max_event_in_iter = 4000 1799 min_event_in_iter = 500 1800 combining_job = sys.maxsize 1801 gen_events_security = 1.00 1802
1803 - def __new__(cls, *args, **opts):
1804 1805 cls.force_class = 'gridpack' 1806 return super(gen_ximprove_gridpack, cls).__new__(cls, *args, **opts)
1807
1808 - def __init__(self, *args, **opts):
1809 1810 self.ngran = -1 1811 self.gscalefact = {} 1812 self.readonly = False 1813 if 'ngran' in opts: 1814 self.gran = opts['ngran'] 1815 # del opts['ngran'] 1816 if 'readonly' in opts: 1817 self.readonly = opts['readonly'] 1818 super(gen_ximprove_gridpack,self).__init__(*args, **opts) 1819 if self.ngran == -1: 1820 self.ngran = 1
1821
1822 - def find_job_for_event(self):
1823 """return the list of channel that need to be improved""" 1824 import random 1825 1826 assert self.err_goal >=1 1827 self.err_goal = int(self.err_goal) 1828 self.gscalefact = {} 1829 1830 xtot = self.results.axsec 1831 goal_lum = self.err_goal/(xtot+1e-99) #pb^-1 1832 # logger.info('Effective Luminosity %s pb^-1', goal_lum) 1833 1834 all_channels = sum([list(P) for P in self.results],[]) 1835 all_channels.sort(key=lambda x : x.get('luminosity'), reverse=True) 1836 1837 to_refine = [] 1838 for C in all_channels: 1839 tag = C.get('name') 1840 self.gscalefact[tag] = 0 1841 R = random.random() 1842 if C.get('axsec') == 0: 1843 continue 1844 if (goal_lum * C.get('axsec') < R*self.ngran ): 1845 continue # no event to generate events 1846 self.gscalefact[tag] = max(1, 1/(goal_lum * C.get('axsec')/ self.ngran)) 1847 #need to generate events 1848 logger.debug('request events for ', C.get('name'), 'cross=', 1849 C.get('axsec'), 'needed events = ', goal_lum * C.get('axsec')) 1850 to_refine.append(C) 1851 1852 logger.info('need to improve %s channels' % len(to_refine)) 1853 return goal_lum, to_refine
1854
1855 - def get_job_for_event(self):
1856 """generate the script in order to generate a given number of event""" 1857 # correspond to write_gen in the fortran version 1858 1859 1860 goal_lum, to_refine = self.find_job_for_event() 1861 1862 jobs = [] # list of the refine if some job are split is list of 1863 # dict with the parameter of the run. 1864 1865 # loop over the channel to refine 1866 for C in to_refine: 1867 #1. Compute the number of points are needed to reach target 1868 needed_event = max(goal_lum*C.get('axsec'), self.ngran) 1869 nb_split = 1 1870 1871 #2. estimate how many points we need in each iteration 1872 if C.get('nunwgt') > 0: 1873 nevents = needed_event / nb_split * (C.get('nevents') / C.get('nunwgt')) 1874 #split by iter 1875 nevents = int(nevents / (2**self.min_iter-1)) 1876 else: 1877 nevents = self.max_event_in_iter 1878 1879 if nevents < self.min_event_in_iter: 1880 nevents = self.min_event_in_iter 1881 # 1882 # forbid too low/too large value 1883 nevents = max(self.min_event_in_iter, min(self.max_event_in_iter, nevents)) 1884 logger.debug("%s : need %s event. Need %s split job of %s points", C.name, needed_event, nb_split, nevents) 1885 1886 1887 #create the info dict assume no splitting for the default 1888 info = {'name': self.cmd.results.current['run_name'], 1889 'script_name': 'unknown', 1890 'directory': C.name, # need to be change for splitted job 1891 'P_dir': os.path.basename(C.parent_name), 1892 'offset': 1, # need to be change for splitted job 1893 'Ppath': pjoin(self.cmd.me_dir, 'SubProcesses', C.parent_name), 1894 'nevents': nevents, #int(nevents*self.gen_events_security)+1, 1895 'maxiter': self.max_iter, 1896 'miniter': self.min_iter, 1897 'precision': -1*int(needed_event)/C.get('axsec'), 1898 'requested_event': needed_event, 1899 'nhel': self.run_card['nhel'], 1900 'channel': C.name.replace('G',''), 1901 'grid_refinment' : 0, #no refinment of the grid 1902 'base_directory': '', #should be change in splitted job if want to keep the grid 1903 'packet': None, 1904 } 1905 1906 1907 jobs.append(info) 1908 1909 1910 write_dir = '.' if self.readonly else None 1911 self.create_ajob(pjoin(self.me_dir, 'SubProcesses', 'refine.sh'), jobs, write_dir) 1912 1913 done = [] 1914 for j in jobs: 1915 if j['P_dir'] in done: 1916 continue 1917 done.append(j['P_dir']) 1918 # set the working directory path. 1919 pwd = pjoin(os.getcwd(),j['P_dir']) if self.readonly else pjoin(self.me_dir, 'SubProcesses', j['P_dir']) 1920 exe = pjoin(pwd, 'ajob1') 1921 st = os.stat(exe) 1922 os.chmod(exe, st.st_mode | stat.S_IEXEC) 1923 1924 # run the code\ 1925 cluster.onecore.launch_and_wait(exe, cwd=pwd, packet_member=j['packet']) 1926 write_dir = '.' if self.readonly else pjoin(self.me_dir, 'SubProcesses') 1927 1928 self.check_events(goal_lum, to_refine, jobs, write_dir)
1929
1930 - def check_events(self, goal_lum, to_refine, jobs, Sdir):
1931 """check that we get the number of requested events if not resubmit.""" 1932 1933 new_jobs = [] 1934 1935 for C, job_info in zip(to_refine, jobs): 1936 P = job_info['P_dir'] 1937 G = job_info['channel'] 1938 axsec = C.get('axsec') 1939 requested_events= job_info['requested_event'] 1940 1941 1942 new_results = sum_html.OneResult((P,G)) 1943 new_results.read_results(pjoin(Sdir,P, 'G%s'%G, 'results.dat')) 1944 1945 # need to resubmit? 1946 if new_results.get('nunwgt') < requested_events: 1947 pwd = pjoin(os.getcwd(),job_info['P_dir'],'G%s'%G) if self.readonly else \ 1948 pjoin(self.me_dir, 'SubProcesses', job_info['P_dir'],'G%s'%G) 1949 job_info['requested_event'] -= new_results.get('nunwgt') 1950 job_info['precision'] -= -1*job_info['requested_event']/axsec 1951 job_info['offset'] += 1 1952 new_jobs.append(job_info) 1953 files.mv(pjoin(pwd, 'events.lhe'), pjoin(pwd, 'events.lhe.previous')) 1954 1955 if new_jobs: 1956 self.create_ajob(pjoin(self.me_dir, 'SubProcesses', 'refine.sh'), new_jobs, Sdir) 1957 1958 done = [] 1959 for j in new_jobs: 1960 if j['P_dir'] in done: 1961 continue 1962 G = j['channel'] 1963 # set the working directory path. 1964 pwd = pjoin(os.getcwd(),j['P_dir']) if self.readonly \ 1965 else pjoin(self.me_dir, 'SubProcesses', j['P_dir']) 1966 exe = pjoin(pwd, 'ajob1') 1967 st = os.stat(exe) 1968 os.chmod(exe, st.st_mode | stat.S_IEXEC) 1969 1970 # run the code 1971 cluster.onecore.launch_and_wait(exe, cwd=pwd, packet_member=j['packet']) 1972 pwd = pjoin(pwd, 'G%s'%G) 1973 # concatanate with old events file 1974 files.put_at_end(pjoin(pwd, 'events.lhe'),pjoin(pwd, 'events.lhe.previous')) 1975 1976 return self.check_events(goal_lum, to_refine, new_jobs, Sdir)
1977