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  import collections 
  22  import os 
  23  import glob 
  24  import logging 
  25  import math 
  26  import re 
  27  import subprocess 
  28  import shutil 
  29   
  30  try: 
  31      import madgraph 
  32  except ImportError: 
  33      MADEVENT = True 
  34      import internal.sum_html as sum_html 
  35      import internal.banner as bannermod 
  36      import internal.misc as misc 
  37      import internal.files as files 
  38      import internal.cluster as cluster 
  39      import internal.combine_grid as combine_grid 
  40      import internal.combine_runs as combine_runs 
  41      import internal.lhe_parser as lhe_parser 
  42  else: 
  43      MADEVENT= False 
  44      import madgraph.madevent.sum_html as sum_html 
  45      import madgraph.various.banner as bannermod 
  46      import madgraph.various.misc as misc 
  47      import madgraph.iolibs.files as files 
  48      import madgraph.various.cluster as cluster 
  49      import madgraph.madevent.combine_grid as combine_grid 
  50      import madgraph.madevent.combine_runs as combine_runs 
  51      import madgraph.various.lhe_parser as lhe_parser 
  52   
  53  logger = logging.getLogger('madgraph.madevent.gen_ximprove') 
  54  pjoin = os.path.join 
55 56 -class gensym(object):
57 """a class to call the fortran gensym executable and handle it's output 58 in order to create the various job that are needed for the survey""" 59 60 #convenient shortcut for the formatting of variable 61 @ staticmethod
62 - def format_variable(*args):
63 return bannermod.ConfigFile.format_variable(*args)
64 65 combining_job = 2 # number of channel by ajob 66 splitted_grid = False 67 min_iterations = 3 68 mode= "survey" 69 70
71 - def __init__(self, cmd, opt=None):
72 73 try: 74 super(gensym, self).__init__(cmd, opt) 75 except TypeError: 76 pass 77 78 # Run statistics, a dictionary of RunStatistics(), with 79 self.run_statistics = {} 80 81 self.cmd = cmd 82 self.run_card = cmd.run_card 83 self.me_dir = cmd.me_dir 84 85 86 # dictionary to keep track of the precision when combining iteration 87 self.cross = collections.defaultdict(int) 88 self.abscross = collections.defaultdict(int) 89 self.sigma = collections.defaultdict(int) 90 self.chi2 = collections.defaultdict(int) 91 92 self.splitted_grid = False 93 if self.cmd.proc_characteristics['loop_induced']: 94 nexternal = self.cmd.proc_characteristics['nexternal'] 95 self.splitted_grid = max(2, (nexternal-2)**2) 96 if hasattr(self.cmd, "opts") and self.cmd.opts['accuracy'] == 0.1: 97 self.cmd.opts['accuracy'] = 0.02 98 99 if isinstance(cmd.cluster, cluster.MultiCore) and self.splitted_grid > 1: 100 self.splitted_grid = int(cmd.cluster.nb_core**0.5) 101 if self.splitted_grid == 1 and cmd.cluster.nb_core >1: 102 self.splitted_grid = 2 103 104 #if the user defines it in the run_card: 105 if self.run_card['survey_splitting'] != -1: 106 self.splitted_grid = self.run_card['survey_splitting'] 107 108 self.splitted_Pdir = {} 109 self.splitted_for_dir = lambda x,y: self.splitted_grid 110 self.combining_job_for_Pdir = lambda x: self.combining_job 111 self.lastoffset = {}
112
113 - def launch(self):
114 """ """ 115 116 self.subproc = [l.strip() for l in open(pjoin(self.me_dir,'SubProcesses', 117 'subproc.mg'))] 118 subproc = self.subproc 119 120 P_zero_result = [] # check the number of times where they are no phase-space 121 122 nb_tot_proc = len(subproc) 123 for nb_proc,subdir in enumerate(subproc): 124 job_list = {} 125 self.cmd.update_status('Compiling for process %s/%s. <br> (previous processes already running)' % \ 126 (nb_proc+1,nb_tot_proc), level=None) 127 128 subdir = subdir.strip() 129 Pdir = pjoin(self.me_dir, 'SubProcesses',subdir) 130 logger.info(' %s ' % subdir) 131 132 # clean previous run 133 for match in misc.glob('*ajob*', Pdir): 134 if os.path.basename(match)[:4] in ['ajob', 'wait', 'run.', 'done']: 135 os.remove(match) 136 for match in misc.glob('G*', Pdir): 137 if os.path.exists(pjoin(match,'results.dat')): 138 os.remove(pjoin(match, 'results.dat')) 139 if os.path.exists(pjoin(match, 'ftn25')): 140 os.remove(pjoin(match, 'ftn25')) 141 142 #compile gensym 143 self.cmd.compile(['gensym'], cwd=Pdir) 144 if not os.path.exists(pjoin(Pdir, 'gensym')): 145 raise Exception, 'Error make gensym not successful' 146 147 # Launch gensym 148 p = misc.Popen(['./gensym'], stdout=subprocess.PIPE, 149 stderr=subprocess.STDOUT, cwd=Pdir) 150 #sym_input = "%(points)d %(iterations)d %(accuracy)f \n" % self.opts 151 (stdout, _) = p.communicate('') 152 153 if os.path.exists(pjoin(self.me_dir,'error')): 154 files.mv(pjoin(self.me_dir,'error'), pjoin(Pdir,'ajob.no_ps.log')) 155 P_zero_result.append(subdir) 156 continue 157 158 jobs = stdout.split() 159 job_list[Pdir] = jobs 160 try: 161 # check that all input are valid 162 [float(s) for s in jobs] 163 except Exception: 164 logger.debug("unformated string found in gensym. Please check:\n %s" % stdout) 165 job_list[Pdir] = [] 166 for s in jobs: 167 try: 168 float(s) 169 except: 170 continue 171 else: 172 job_list[Pdir].append(s) 173 174 self.cmd.compile(['madevent'], cwd=Pdir) 175 self.submit_to_cluster(job_list) 176 return job_list, P_zero_result
177 178
179 - def submit_to_cluster(self, job_list):
180 """ """ 181 182 if self.run_card['job_strategy'] > 0: 183 if len(job_list) >1: 184 for path, dirs in job_list.items(): 185 self.submit_to_cluster({path:dirs}) 186 return 187 path, value = job_list.items()[0] 188 nexternal = self.cmd.proc_characteristics['nexternal'] 189 current = open(pjoin(path, "nexternal.inc")).read() 190 ext = re.search(r"PARAMETER \(NEXTERNAL=(\d+)\)", current).group(1) 191 192 if self.run_card['job_strategy'] == 2: 193 self.splitted_grid = 2 194 if nexternal == int(ext): 195 to_split = 2 196 else: 197 to_split = 0 198 if hasattr(self, 'splitted_Pdir'): 199 self.splitted_Pdir[path] = to_split 200 else: 201 self.splitted_Pdir = {path: to_split} 202 self.splitted_for_dir = lambda x,y : self.splitted_Pdir[x] 203 elif self.run_card['job_strategy'] == 1: 204 if nexternal == int(ext): 205 combine = 1 206 else: 207 combine = self.combining_job 208 if hasattr(self, 'splitted_Pdir'): 209 self.splitted_Pdir[path] = combine 210 else: 211 self.splitted_Pdir = {path: combine} 212 self.combining_job_for_Pdir = lambda x : self.splitted_Pdir[x] 213 214 if not self.splitted_grid: 215 return self.submit_to_cluster_no_splitting(job_list) 216 elif self.cmd.cluster_mode == 0: 217 return self.submit_to_cluster_no_splitting(job_list) 218 elif self.cmd.cluster_mode == 2 and self.cmd.options['nb_core'] == 1: 219 return self.submit_to_cluster_no_splitting(job_list) 220 else: 221 return self.submit_to_cluster_splitted(job_list)
222 223
224 - def submit_to_cluster_no_splitting(self, job_list):
225 """submit the survey without the parralelization. 226 This is the old mode which is still usefull in single core""" 227 228 # write the template file for the parameter file 229 self.write_parameter(parralelization=False, Pdirs=job_list.keys()) 230 231 232 # launch the job with the appropriate grouping 233 for Pdir, jobs in job_list.items(): 234 jobs = list(jobs) 235 i=0 236 while jobs: 237 i+=1 238 to_submit = ['0'] # the first entry is actually the offset 239 for _ in range(self.combining_job_for_Pdir(Pdir)): 240 if jobs: 241 to_submit.append(jobs.pop(0)) 242 243 self.cmd.launch_job(pjoin(self.me_dir, 'SubProcesses', 'survey.sh'), 244 argument=to_submit, 245 cwd=pjoin(self.me_dir,'SubProcesses' , Pdir))
246 247
248 - def create_resubmit_one_iter(self, Pdir, G, submit_ps, nb_job, step=0):
249 """prepare the input_file for submitting the channel""" 250 251 252 if 'SubProcesses' not in Pdir: 253 Pdir = pjoin(self.me_dir, 'SubProcesses', Pdir) 254 255 #keep track of how many job are sended 256 self.splitted_Pdir[(Pdir, G)] = int(nb_job) 257 258 259 # 1. write the new input_app.txt 260 run_card = self.cmd.run_card 261 options = {'event' : submit_ps, 262 'maxiter': 1, 263 'miniter': 1, 264 'accuracy': self.cmd.opts['accuracy'], 265 'helicity': run_card['nhel_survey'] if 'nhel_survey' in run_card \ 266 else run_card['nhel'], 267 'gridmode': -2, 268 'channel' : G 269 } 270 271 Gdir = pjoin(Pdir, 'G%s' % G) 272 self.write_parameter_file(pjoin(Gdir, 'input_app.txt'), options) 273 274 # 2. check that ftn25 exists. 275 assert os.path.exists(pjoin(Gdir, "ftn25")) 276 277 278 # 3. Submit the new jobs 279 #call back function 280 packet = cluster.Packet((Pdir, G, step+1), 281 self.combine_iteration, 282 (Pdir, G, step+1)) 283 284 if step ==0: 285 self.lastoffset[(Pdir, G)] = 0 286 287 # resubmit the new jobs 288 for i in xrange(int(nb_job)): 289 name = "G%s_%s" % (G,i+1) 290 self.lastoffset[(Pdir, G)] += 1 291 offset = self.lastoffset[(Pdir, G)] 292 self.cmd.launch_job(pjoin(self.me_dir, 'SubProcesses', 'refine_splitted.sh'), 293 argument=[name, 'G%s'%G, offset], 294 cwd= Pdir, 295 packet_member=packet)
296 297
298 - def submit_to_cluster_splitted(self, job_list):
299 """ submit the version of the survey with splitted grid creation 300 """ 301 302 #if self.splitted_grid <= 1: 303 # return self.submit_to_cluster_no_splitting(job_list) 304 305 for Pdir, jobs in job_list.items(): 306 if self.splitted_for_dir(Pdir, jobs[0]) <= 1: 307 return self.submit_to_cluster_no_splitting({Pdir:jobs}) 308 309 self.write_parameter(parralelization=True, Pdirs=[Pdir]) 310 # launch the job with the appropriate grouping 311 312 for job in jobs: 313 packet = cluster.Packet((Pdir, job, 1), self.combine_iteration, (Pdir, job, 1)) 314 for i in range(self.splitted_for_dir(Pdir, job)): 315 self.cmd.launch_job(pjoin(self.me_dir, 'SubProcesses', 'survey.sh'), 316 argument=[i+1, job], 317 cwd=pjoin(self.me_dir,'SubProcesses' , Pdir), 318 packet_member=packet)
319
320 - def combine_iteration(self, Pdir, G, step):
321 322 grid_calculator, cross, error = self.combine_grid(Pdir, G, step) 323 324 # Compute the number of events used for this run. 325 nb_events = grid_calculator.target_evt 326 327 Gdirs = [] #build the the list of directory 328 for i in range(self.splitted_for_dir(Pdir, G)): 329 path = pjoin(Pdir, "G%s_%s" % (G, i+1)) 330 Gdirs.append(path) 331 332 # 4. make the submission of the next iteration 333 # Three cases - less than 3 iteration -> continue 334 # - more than 3 and less than 5 -> check error 335 # - more than 5 -> prepare info for refine 336 need_submit = False 337 if step < self.min_iterations and cross != 0: 338 if step == 1: 339 need_submit = True 340 else: 341 across = self.abscross[(Pdir,G)]/(self.sigma[(Pdir,G)]+1e-99) 342 tot_across = self.get_current_axsec() 343 if across / tot_across < 1e-6: 344 need_submit = False 345 elif error < self.cmd.opts['accuracy'] / 100: 346 need_submit = False 347 else: 348 need_submit = True 349 350 elif step >= self.cmd.opts['iterations']: 351 need_submit = False 352 elif self.cmd.opts['accuracy'] < 0: 353 #check for luminosity 354 raise Exception, "Not Implemented" 355 elif self.abscross[(Pdir,G)] == 0: 356 need_submit = False 357 else: 358 across = self.abscross[(Pdir,G)]/(self.sigma[(Pdir,G)]+1e-99) 359 tot_across = self.get_current_axsec() 360 if across == 0: 361 need_submit = False 362 elif across / tot_across < 1e-5: 363 need_submit = False 364 elif error > self.cmd.opts['accuracy']: 365 need_submit = True 366 else: 367 need_submit = False 368 369 370 if cross: 371 grid_calculator.write_grid_for_submission(Pdir,G, 372 self.splitted_for_dir(Pdir, G), 373 nb_events,mode=self.mode, 374 conservative_factor=5.0) 375 376 xsec_format = '.%ig'%(max(3,int(math.log10(1.0/float(error)))+2) 377 if float(cross)!=0.0 and float(error)!=0.0 else 8) 378 if need_submit: 379 message = "%%s/G%%s is at %%%s +- %%.3g pb. Now submitting iteration #%s."%(xsec_format, step+1) 380 logger.info(message%\ 381 (os.path.basename(Pdir), G, float(cross), 382 float(error)*float(cross))) 383 self.resubmit_survey(Pdir,G, Gdirs, step) 384 elif cross: 385 logger.info("Survey finished for %s/G%s at %s"%( 386 os.path.basename(Pdir),G,('%%%s +- %%.3g pb'%xsec_format))% 387 (float(cross), float(error)*float(cross))) 388 # prepare information for refine 389 newGpath = pjoin(self.me_dir,'SubProcesses' , Pdir, 'G%s' % G) 390 if not os.path.exists(newGpath): 391 os.mkdir(newGpath) 392 393 # copy the new grid: 394 files.cp(pjoin(Gdirs[0], 'ftn25'), 395 pjoin(self.me_dir,'SubProcesses' , Pdir, 'G%s' % G, 'ftn26')) 396 397 # copy the events 398 fsock = open(pjoin(newGpath, 'events.lhe'), 'w') 399 for Gdir in Gdirs: 400 fsock.write(open(pjoin(Gdir, 'events.lhe')).read()) 401 402 # copy one log 403 files.cp(pjoin(Gdirs[0], 'log.txt'), 404 pjoin(self.me_dir,'SubProcesses' , Pdir, 'G%s' % G)) 405 406 407 # create the appropriate results.dat 408 self.write_results(grid_calculator, cross, error, Pdir, G, step) 409 else: 410 logger.info("Survey finished for %s/G%s [0 cross]", os.path.basename(Pdir),G) 411 412 Gdir = pjoin(self.me_dir,'SubProcesses' , Pdir, 'G%s' % G) 413 if not os.path.exists(Gdir): 414 os.mkdir(Gdir) 415 # copy one log 416 files.cp(pjoin(Gdirs[0], 'log.txt'), Gdir) 417 # create the appropriate results.dat 418 self.write_results(grid_calculator, cross, error, Pdir, G, step) 419 420 return 0
421
422 - def combine_grid(self, Pdir, G, step, exclude_sub_jobs=[]):
423 """ exclude_sub_jobs is to remove some of the subjobs if a numerical 424 issue is detected in one of them. Warning is issue when this occurs. 425 """ 426 427 # 1. create an object to combine the grid information and fill it 428 grid_calculator = combine_grid.grid_information(self.run_card['nhel']) 429 430 for i in range(self.splitted_for_dir(Pdir, G)): 431 if i in exclude_sub_jobs: 432 continue 433 path = pjoin(Pdir, "G%s_%s" % (G, i+1)) 434 fsock = misc.mult_try_open(pjoin(path, 'results.dat')) 435 one_result = grid_calculator.add_results_information(fsock) 436 fsock.close() 437 if one_result.axsec == 0: 438 grid_calculator.onefail = True 439 continue # grid_information might not exists 440 fsock = misc.mult_try_open(pjoin(path, 'grid_information')) 441 grid_calculator.add_one_grid_information(fsock) 442 fsock.close() 443 os.remove(pjoin(path, 'results.dat')) 444 #os.remove(pjoin(path, 'grid_information')) 445 446 447 448 #2. combine the information about the total crossection / error 449 # start by keep the interation in memory 450 cross, across, sigma = grid_calculator.get_cross_section() 451 452 #3. Try to avoid one single PS point which ruins the integration 453 # Should be related to loop evaluation instability. 454 maxwgt = grid_calculator.get_max_wgt(0.01) 455 if maxwgt: 456 nunwgt = grid_calculator.get_nunwgt(maxwgt) 457 # Make sure not to apply the security below during the first step of the 458 # survey. Also, disregard channels with a contribution relative to the 459 # total cross-section smaller than 1e-8 since in this case it is unlikely 460 # that this channel will need more than 1 event anyway. 461 apply_instability_security = False 462 rel_contrib = 0.0 463 if (self.__class__ != gensym or step > 1): 464 Pdir_across = 0.0 465 Gdir_across = 0.0 466 for (mPdir,mG) in self.abscross.keys(): 467 if mPdir == Pdir: 468 Pdir_across += (self.abscross[(mPdir,mG)]/ 469 (self.sigma[(mPdir,mG)]+1e-99)) 470 if mG == G: 471 Gdir_across += (self.abscross[(mPdir,mG)]/ 472 (self.sigma[(mPdir,mG)]+1e-99)) 473 rel_contrib = abs(Gdir_across/(Pdir_across+1e-99)) 474 if rel_contrib > (1.0e-8) and \ 475 nunwgt < 2 and len(grid_calculator.results) > 1: 476 apply_instability_security = True 477 478 if apply_instability_security: 479 # check the ratio between the different submit 480 th_maxwgt = [(r.th_maxwgt,i) for i,r in enumerate(grid_calculator.results)] 481 th_maxwgt.sort() 482 ratio = th_maxwgt[-1][0]/th_maxwgt[-2][0] 483 if ratio > 1e4: 484 logger.warning( 485 """"One Event with large weight have been found (ratio = %.3g) in channel G%s (with rel.contrib=%.3g). 486 This is likely due to numerical instabilities. The associated job is discarded to recover. 487 For offline investigation, the problematic discarded events are stored in: 488 %s"""%(ratio,G,rel_contrib,pjoin(Pdir,'DiscardedUnstableEvents'))) 489 exclude_sub_jobs = list(exclude_sub_jobs) 490 exclude_sub_jobs.append(th_maxwgt[-1][1]) 491 grid_calculator.results.run_statistics['skipped_subchannel'] += 1 492 493 # Add some monitoring of the problematic events 494 gPath = pjoin(Pdir, "G%s_%s" % (G, th_maxwgt[-1][1]+1)) 495 if os.path.isfile(pjoin(gPath,'events.lhe')): 496 lhe_file = lhe_parser.EventFile(pjoin(gPath,'events.lhe')) 497 discardedPath = pjoin(Pdir,'DiscardedUnstableEvents') 498 if not os.path.exists(discardedPath): 499 os.mkdir(discardedPath) 500 if os.path.isdir(discardedPath): 501 # Keep only the event with a maximum weight, as it surely 502 # is the problematic one. 503 evtRecord = open(pjoin(discardedPath,'discarded_G%s.dat'%G),'a') 504 lhe_file.seek(0) #rewind the file 505 try: 506 evtRecord.write('\n'+str(max(lhe_file,key=lambda evt:abs(evt.wgt)))) 507 except Exception: 508 #something wrong write the full file. 509 lhe_file.close() 510 evtRecord.write(pjoin(gPath,'events.lhe').read()) 511 evtRecord.close() 512 513 return self.combine_grid(Pdir, G, step, exclude_sub_jobs) 514 515 516 if across !=0: 517 if sigma != 0: 518 self.cross[(Pdir,G)] += cross**3/sigma**2 519 self.abscross[(Pdir,G)] += across * cross**2/sigma**2 520 self.sigma[(Pdir,G)] += cross**2/ sigma**2 521 self.chi2[(Pdir,G)] += cross**4/sigma**2 522 # and use those iteration to get the current estimator 523 cross = self.cross[(Pdir,G)]/self.sigma[(Pdir,G)] 524 if step > 1: 525 error = math.sqrt(abs((self.chi2[(Pdir,G)]/cross**2 - \ 526 self.sigma[(Pdir,G)])/(step-1))/self.sigma[(Pdir,G)]) 527 else: 528 error = sigma/cross 529 else: 530 self.cross[(Pdir,G)] = cross 531 self.abscross[(Pdir,G)] = across 532 self.sigma[(Pdir,G)] = 0 533 self.chi2[(Pdir,G)] = 0 534 cross = self.cross[(Pdir,G)] 535 error = 0 536 537 else: 538 error = 0 539 540 grid_calculator.results.compute_values(update_statistics=True) 541 if (str(os.path.basename(Pdir)), G) in self.run_statistics: 542 self.run_statistics[(str(os.path.basename(Pdir)), G)]\ 543 .aggregate_statistics(grid_calculator.results.run_statistics) 544 else: 545 self.run_statistics[(str(os.path.basename(Pdir)), G)] = \ 546 grid_calculator.results.run_statistics 547 548 self.warnings_from_statistics(G, grid_calculator.results.run_statistics) 549 stats_msg = grid_calculator.results.run_statistics.nice_output( 550 '/'.join([os.path.basename(Pdir),'G%s'%G])) 551 552 if stats_msg: 553 logger.log(5, stats_msg) 554 555 # Clean up grid_information to avoid border effects in case of a crash 556 for i in range(self.splitted_for_dir(Pdir, G)): 557 path = pjoin(Pdir, "G%s_%s" % (G, i+1)) 558 try: 559 os.remove(pjoin(path, 'grid_information')) 560 except OSError, oneerror: 561 if oneerror.errno != 2: 562 raise 563 return grid_calculator, cross, error
564
565 - def warnings_from_statistics(self,G,stats):
566 """Possible warn user for worrying MadLoop stats for this channel.""" 567 568 if stats['n_madloop_calls']==0: 569 return 570 571 EPS_fraction = float(stats['exceptional_points'])/stats['n_madloop_calls'] 572 573 msg = "Channel %s has encountered a fraction of %.3g\n"+ \ 574 "of numerically unstable loop matrix element computations\n"+\ 575 "(which could not be rescued using quadruple precision).\n"+\ 576 "The results might not be trusted." 577 578 if 0.01 > EPS_fraction > 0.001: 579 logger.warning(msg%(G,EPS_fraction)) 580 elif EPS_fraction > 0.01: 581 logger.critical((msg%(G,EPS_fraction)).replace('might', 'can')) 582 raise Exception, (msg%(G,EPS_fraction)).replace('might', 'can')
583
584 - def get_current_axsec(self):
585 586 across = 0 587 for (Pdir,G) in self.abscross: 588 across += self.abscross[(Pdir,G)]/(self.sigma[(Pdir,G)]+1e-99) 589 return across
590
591 - def write_results(self, grid_calculator, cross, error, Pdir, G, step):
592 593 #compute the value 594 if cross == 0: 595 abscross,nw, luminosity = 0, 0, 0 596 wgt, maxit,nunwgt, wgt, nevents = 0,0,0,0,0 597 maxwgt = 0 598 error = 0 599 else: 600 grid_calculator.results.compute_values() 601 abscross = self.abscross[(Pdir,G)]/self.sigma[(Pdir,G)] 602 nw = grid_calculator.results.nw 603 wgt = grid_calculator.results.wgt 604 maxit = step 605 wgt = 0 606 nevents = grid_calculator.results.nevents 607 maxwgt = grid_calculator.get_max_wgt() 608 nunwgt = grid_calculator.get_nunwgt() 609 luminosity = nunwgt/cross 610 611 #format the results.dat 612 def fstr(nb): 613 data = '%E' % nb 614 nb, power = data.split('E') 615 nb = float(nb) /10 616 power = int(power) + 1 617 return '%.5fE%+03i' %(nb,power)
618 line = '%s %s %s %i %i %i %i %s %s %s %s 0.0 0\n' % \ 619 (fstr(cross), fstr(error*cross), fstr(error*cross), 620 nevents, nw, maxit,nunwgt, 621 fstr(luminosity), fstr(wgt), fstr(abscross), fstr(maxwgt)) 622 623 fsock = open(pjoin(self.me_dir,'SubProcesses' , Pdir, 'G%s' % G, 624 'results.dat'),'w') 625 fsock.writelines(line) 626 fsock.close()
627
628 - def resubmit_survey(self, Pdir, G, Gdirs, step):
629 """submit the next iteration of the survey""" 630 631 # 1. write the new input_app.txt to double the number of points 632 run_card = self.cmd.run_card 633 options = {'event' : 2**(step) * self.cmd.opts['points'] / self.splitted_grid, 634 'maxiter': 1, 635 'miniter': 1, 636 'accuracy': self.cmd.opts['accuracy'], 637 'helicity': run_card['nhel_survey'] if 'nhel_survey' in run_card \ 638 else run_card['nhel'], 639 'gridmode': -2, 640 'channel' : '' 641 } 642 643 if int(options['helicity']) == 1: 644 options['event'] = options['event'] * 2**(self.cmd.proc_characteristics['nexternal']//3) 645 646 for Gdir in Gdirs: 647 self.write_parameter_file(pjoin(Gdir, 'input_app.txt'), options) 648 649 650 #2. resubmit the new jobs 651 packet = cluster.Packet((Pdir, G, step+1), self.combine_iteration, \ 652 (Pdir, G, step+1)) 653 nb_step = len(Gdirs) * (step+1) 654 for i,subdir in enumerate(Gdirs): 655 subdir = subdir.rsplit('_',1)[1] 656 subdir = int(subdir) 657 offset = nb_step+i+1 658 offset=str(offset) 659 tag = "%s.%s" % (subdir, offset) 660 661 self.cmd.launch_job(pjoin(self.me_dir, 'SubProcesses', 'survey.sh'), 662 argument=[tag, G], 663 cwd=pjoin(self.me_dir,'SubProcesses' , Pdir), 664 packet_member=packet)
665 666 667 668
669 - def write_parameter_file(self, path, options):
670 """ """ 671 672 template =""" %(event)s %(maxiter)s %(miniter)s !Number of events and max and min iterations 673 %(accuracy)s !Accuracy 674 %(gridmode)s !Grid Adjustment 0=none, 2=adjust 675 1 !Suppress Amplitude 1=yes 676 %(helicity)s !Helicity Sum/event 0=exact 677 %(channel)s """ 678 options['event'] = int(options['event']) 679 open(path, 'w').write(template % options)
680 681 682
683 - def write_parameter(self, parralelization, Pdirs=None):
684 """Write the parameter of the survey run""" 685 686 run_card = self.cmd.run_card 687 688 options = {'event' : self.cmd.opts['points'], 689 'maxiter': self.cmd.opts['iterations'], 690 'miniter': self.min_iterations, 691 'accuracy': self.cmd.opts['accuracy'], 692 'helicity': run_card['nhel_survey'] if 'nhel_survey' in run_card \ 693 else run_card['nhel'], 694 'gridmode': 2, 695 'channel': '' 696 } 697 698 if int(options['helicity'])== 1: 699 options['event'] = options['event'] * 2**(self.cmd.proc_characteristics['nexternal']//3) 700 701 if parralelization: 702 options['gridmode'] = -2 703 options['maxiter'] = 1 #this is automatic in dsample anyway 704 options['miniter'] = 1 #this is automatic in dsample anyway 705 options['event'] /= self.splitted_grid 706 707 if not Pdirs: 708 Pdirs = self.subproc 709 710 for Pdir in Pdirs: 711 path =pjoin(Pdir, 'input_app.txt') 712 self.write_parameter_file(path, options)
713
714 715 716 -class gen_ximprove(object):
717 718 719 # some hardcoded value which impact the generation 720 gen_events_security = 1.2 # multiply the number of requested event by this number for security 721 combining_job = 0 # allow to run multiple channel in sequence 722 max_request_event = 1000 # split jobs if a channel if it needs more than that 723 max_event_in_iter = 5000 724 min_event_in_iter = 1000 725 max_splitting = 130 # maximum duplication of a given channel 726 min_iter = 3 727 max_iter = 9 728 keep_grid_for_refine = False # only apply if needed to split the job 729 730 #convenient shortcut for the formatting of variable 731 @ staticmethod
732 - def format_variable(*args):
733 return bannermod.ConfigFile.format_variable(*args)
734 735
736 - def __new__(cls, cmd, opt):
737 """Choose in which type of refine we want to be""" 738 739 if cmd.proc_characteristics['loop_induced']: 740 return super(gen_ximprove, cls).__new__(gen_ximprove_share, cmd, opt) 741 elif gen_ximprove.format_variable(cmd.run_card['gridpack'], bool): 742 raise Exception, "Not implemented" 743 elif cmd.run_card["job_strategy"] == 2: 744 return super(gen_ximprove, cls).__new__(gen_ximprove_share, cmd, opt) 745 else: 746 return super(gen_ximprove, cls).__new__(gen_ximprove_v4, cmd, opt)
747 748
749 - def __init__(self, cmd, opt=None):
750 751 try: 752 super(gen_ximprove, self).__init__(cmd, opt) 753 except TypeError: 754 pass 755 756 self.run_statistics = {} 757 self.cmd = cmd 758 self.run_card = cmd.run_card 759 run_card = self.run_card 760 self.me_dir = cmd.me_dir 761 762 #extract from the run_card the information that we need. 763 self.gridpack = run_card['gridpack'] 764 self.nhel = run_card['nhel'] 765 if "nhel_refine" in run_card: 766 self.nhel = run_card["nhel_refine"] 767 768 if self.run_card['refine_evt_by_job'] != -1: 769 self.max_request_event = run_card['refine_evt_by_job'] 770 771 772 # Default option for the run 773 self.gen_events = True 774 self.min_iter = 3 775 self.parralel = False 776 # parameter which was input for the normal gen_ximprove run 777 self.err_goal = 0.01 778 self.max_np = 9 779 self.split_channels = False 780 # parameter for the gridpack run 781 self.nreq = 2000 782 self.iseed = 4321 783 self.ngran = 1 784 785 # placeholder for information 786 self.results = 0 #updated in launch/update_html 787 788 if isinstance(opt, dict): 789 self.configure(opt) 790 elif isinstance(opt, bannermod.GridpackCard): 791 self.configure_gridpack(opt)
792
793 - def __call__(self):
794 return self.launch()
795
796 - def launch(self):
797 """running """ 798 799 #start the run 800 self.handle_seed() 801 802 self.results = sum_html.collect_result(self.cmd, None) 803 804 if self.gen_events: 805 # We run to provide a given number of events 806 self.get_job_for_event() 807 else: 808 # We run to achieve a given precision 809 self.get_job_for_precision()
810 811
812 - def configure(self, opt):
813 """Defines some parameter of the run""" 814 815 for key, value in opt.items(): 816 if key in self.__dict__: 817 targettype = type(getattr(self, key)) 818 setattr(self, key, self.format_variable(value, targettype, key)) 819 else: 820 raise Exception, '%s not define' % key 821 822 823 # special treatment always do outside the loop to avoid side effect 824 if 'err_goal' in opt: 825 if self.err_goal < 1: 826 logger.info("running for accuracy %s%%" % (self.err_goal*100)) 827 self.gen_events = False 828 elif self.err_goal >= 1: 829 logger.info("Generating %s unweigthed events." % self.err_goal) 830 self.gen_events = True 831 self.err_goal = self.err_goal * self.gen_events_security # security
832
833 - def handle_seed(self):
834 """not needed but for gridpack --which is not handle here for the moment""" 835 return
836 837
838 - def find_job_for_event(self):
839 """return the list of channel that need to be improved""" 840 841 assert self.err_goal >=1 842 self.err_goal = int(self.err_goal) 843 844 goal_lum = self.err_goal/(self.results.axsec+1e-99) #pb^-1 845 logger.info('Effective Luminosity %s pb^-1', goal_lum) 846 847 all_channels = sum([list(P) for P in self.results],[]) 848 all_channels.sort(cmp= lambda x,y: 1 if y.get('luminosity') - \ 849 x.get('luminosity') > 0 else -1) 850 851 to_refine = [] 852 for C in all_channels: 853 if C.get('axsec') == 0: 854 continue 855 if goal_lum/(C.get('luminosity')+1e-99) >= 1 + (self.gen_events_security-1)/2: 856 logger.debug("channel %s is at %s (%s) (%s pb)", C.name, C.get('luminosity'), goal_lum/(C.get('luminosity')+1e-99), C.get('xsec')) 857 to_refine.append(C) 858 elif C.get('xerr') > max(C.get('axsec'), 859 (1/(100*math.sqrt(self.err_goal)))*all_channels[-1].get('axsec')): 860 to_refine.append(C) 861 862 logger.info('need to improve %s channels' % len(to_refine)) 863 return goal_lum, to_refine
864
865 - def update_html(self):
866 """update the html from this object since it contains all the information""" 867 868 869 run = self.cmd.results.current['run_name'] 870 if not os.path.exists(pjoin(self.cmd.me_dir, 'HTML', run)): 871 os.mkdir(pjoin(self.cmd.me_dir, 'HTML', run)) 872 873 unit = self.cmd.results.unit 874 P_text = "" 875 if self.results: 876 Presults = self.results 877 else: 878 self.results = sum_html.collect_result(self.cmd, None) 879 Presults = self.results 880 881 for P_comb in Presults: 882 P_text += P_comb.get_html(run, unit, self.cmd.me_dir) 883 884 Presults.write_results_dat(pjoin(self.cmd.me_dir,'SubProcesses', 'results.dat')) 885 886 fsock = open(pjoin(self.cmd.me_dir, 'HTML', run, 'results.html'),'w') 887 fsock.write(sum_html.results_header) 888 fsock.write('%s <dl>' % Presults.get_html(run, unit, self.cmd.me_dir)) 889 fsock.write('%s </dl></body>' % P_text) 890 891 self.cmd.results.add_detail('cross', Presults.xsec) 892 self.cmd.results.add_detail('error', Presults.xerru) 893 894 return Presults.xsec, Presults.xerru
895
896 897 -class gen_ximprove_v4(gen_ximprove):
898 899 # some hardcoded value which impact the generation 900 gen_events_security = 1.2 # multiply the number of requested event by this number for security 901 combining_job = 0 # allow to run multiple channel in sequence 902 max_request_event = 1000 # split jobs if a channel if it needs more than that 903 max_event_in_iter = 5000 904 min_event_in_iter = 1000 905 max_splitting = 130 # maximum duplication of a given channel 906 min_iter = 3 907 max_iter = 9 908 keep_grid_for_refine = False # only apply if needed to split the job 909 910 911
912 - def __init__(self, cmd, opt=None):
913 914 super(gen_ximprove_v4, self).__init__(cmd, opt) 915 916 if cmd.opts['accuracy'] < cmd._survey_options['accuracy'][1]: 917 self.increase_precision()
918
919 - def reset_multijob(self):
920 921 for path in misc.glob(pjoin('*', '*','multijob.dat'), pjoin(self.me_dir, 'SubProcesses')): 922 open(path,'w').write('0\n')
923
924 - def write_multijob(self, Channel, nb_split):
925 """ """ 926 if nb_split <=1: 927 return 928 f = open(pjoin(self.me_dir, 'SubProcesses', Channel.get('name'), 'multijob.dat'), 'w') 929 f.write('%i\n' % nb_split) 930 f.close()
931
932 - def increase_precision(self):
933 934 self.max_event_in_iter = 20000 935 self.min_events = 7500 936 if int(self.nhel) == 1: 937 self.min_event_in_iter *= 2**(self.cmd.proc_characteristics['nexternal']//3) 938 self.max_event_in_iter *= 2**(self.cmd.proc_characteristics['nexternal']//2) 939 940 self.gen_events_security = 1.3
941 942 alphabet = "abcdefghijklmnopqrstuvwxyz"
943 - def get_job_for_event(self):
944 """generate the script in order to generate a given number of event""" 945 # correspond to write_gen in the fortran version 946 947 948 goal_lum, to_refine = self.find_job_for_event() 949 950 #reset the potential multijob of previous run 951 self.reset_multijob() 952 953 jobs = [] # list of the refine if some job are split is list of 954 # dict with the parameter of the run. 955 956 # try to have a smart load on the cluster (not really important actually) 957 if self.combining_job >1: 958 # add a nice ordering for the jobs 959 new_order = [] 960 if self.combining_job % 2 == 0: 961 for i in range(len(to_refine) //2): 962 new_order.append(to_refine[i]) 963 new_order.append(to_refine[-i-1]) 964 if len(to_refine) % 2: 965 new_order.append(to_refine[i+1]) 966 else: 967 for i in range(len(to_refine) //3): 968 new_order.append(to_refine[i]) 969 new_order.append(to_refine[-2*i-1]) 970 new_order.append(to_refine[-2*i-2]) 971 if len(to_refine) % 3 == 1: 972 new_order.append(to_refine[i+1]) 973 elif len(to_refine) % 3 == 2: 974 new_order.append(to_refine[i+2]) 975 #ensure that the reordering is done nicely 976 assert set([id(C) for C in to_refine]) == set([id(C) for C in new_order]) 977 to_refine = new_order 978 979 980 # loop over the channel to refine 981 for C in to_refine: 982 #1. Compute the number of points are needed to reach target 983 needed_event = goal_lum*C.get('axsec') 984 nb_split = int(max(1,((needed_event-1)// self.max_request_event) +1)) 985 if not self.split_channels: 986 nb_split = 1 987 if nb_split > self.max_splitting: 988 nb_split = self.max_splitting 989 nb_split=max(1, nb_split) 990 991 992 #2. estimate how many points we need in each iteration 993 if C.get('nunwgt') > 0: 994 nevents = needed_event / nb_split * (C.get('nevents') / C.get('nunwgt')) 995 #split by iter 996 nevents = int(nevents / (2**self.min_iter-1)) 997 else: 998 nevents = self.max_event_in_iter 999 1000 if nevents < self.min_event_in_iter: 1001 nb_split = int(nb_split * nevents / self.min_event_in_iter) + 1 1002 nevents = self.min_event_in_iter 1003 # 1004 # forbid too low/too large value 1005 nevents = max(self.min_event_in_iter, min(self.max_event_in_iter, nevents)) 1006 logger.debug("%s : need %s event. Need %s split job of %s points", C.name, needed_event, nb_split, nevents) 1007 1008 1009 # write the multi-job information 1010 self.write_multijob(C, nb_split) 1011 1012 packet = cluster.Packet((C.parent_name, C.name), 1013 combine_runs.CombineRuns, 1014 (pjoin(self.me_dir, 'SubProcesses', C.parent_name)), 1015 {"subproc": C.name, "nb_split":nb_split}) 1016 1017 1018 #create the info dict assume no splitting for the default 1019 info = {'name': self.cmd.results.current['run_name'], 1020 'script_name': 'unknown', 1021 'directory': C.name, # need to be change for splitted job 1022 'P_dir': C.parent_name, 1023 'offset': 1, # need to be change for splitted job 1024 'nevents': nevents, 1025 'maxiter': self.max_iter, 1026 'miniter': self.min_iter, 1027 'precision': -goal_lum/nb_split, 1028 'nhel': self.run_card['nhel'], 1029 'channel': C.name.replace('G',''), 1030 'grid_refinment' : 0, #no refinment of the grid 1031 'base_directory': '', #should be change in splitted job if want to keep the grid 1032 'packet': packet, 1033 } 1034 1035 if nb_split == 1: 1036 jobs.append(info) 1037 else: 1038 for i in range(nb_split): 1039 new_info = dict(info) 1040 new_info['offset'] = i+1 1041 new_info['directory'] += self.alphabet[i % 26] + str((i+1)//26) 1042 if self.keep_grid_for_refine: 1043 new_info['base_directory'] = info['directory'] 1044 jobs.append(new_info) 1045 1046 self.create_ajob(pjoin(self.me_dir, 'SubProcesses', 'refine.sh'), jobs)
1047 1048
1049 - def create_ajob(self, template, jobs):
1050 """create the ajob""" 1051 1052 if not jobs: 1053 return 1054 1055 #filter the job according to their SubProcess directory # no mix submition 1056 P2job= collections.defaultdict(list) 1057 for j in jobs: 1058 P2job[j['P_dir']].append(j) 1059 if len(P2job) >1: 1060 for P in P2job.values(): 1061 self.create_ajob(template, P) 1062 return 1063 1064 #Here we can assume that all job are for the same directory. 1065 path = pjoin(self.me_dir, 'SubProcesses' ,jobs[0]['P_dir']) 1066 1067 template_text = open(template, 'r').read() 1068 # special treatment if needed to combine the script 1069 # computes how many submition miss one job 1070 if self.combining_job > 1: 1071 skip1=0 1072 n_channels = len(jobs) 1073 nb_sub = n_channels // self.combining_job 1074 nb_job_in_last = n_channels % self.combining_job 1075 if nb_job_in_last: 1076 nb_sub +=1 1077 skip1 = self.combining_job - nb_job_in_last 1078 if skip1 > nb_sub: 1079 self.combining_job -=1 1080 return self.create_ajob(template, jobs) 1081 combining_job = self.combining_job 1082 else: 1083 #define the variable for combining jobs even in not combine mode 1084 #such that we can use the same routine 1085 skip1=0 1086 combining_job =1 1087 nb_sub = len(jobs) 1088 1089 1090 nb_use = 0 1091 for i in range(nb_sub): 1092 script_number = i+1 1093 if i < skip1: 1094 nb_job = combining_job -1 1095 else: 1096 nb_job = combining_job 1097 fsock = open(pjoin(path, 'ajob%i' % script_number), 'w') 1098 for j in range(nb_use, nb_use + nb_job): 1099 if j> len(jobs): 1100 break 1101 info = jobs[j] 1102 info['script_name'] = 'ajob%i' % script_number 1103 info['keeplog'] = 'false' 1104 if "base_directory" not in info: 1105 info["base_directory"] = "./" 1106 fsock.write(template_text % info) 1107 nb_use += nb_job
1108
1109 - def get_job_for_precision(self):
1110 """create the ajob to achieve a give precision on the total cross-section""" 1111 1112 1113 assert self.err_goal <=1 1114 xtot = abs(self.results.xsec) 1115 logger.info("Working on precision: %s %%" %(100*self.err_goal)) 1116 all_channels = sum([list(P) for P in self.results if P.mfactor],[]) 1117 limit = self.err_goal * xtot / len(all_channels) 1118 to_refine = [] 1119 rerr = 0 #error of the job not directly selected 1120 for C in all_channels: 1121 cerr = C.mfactor*(C.xerru + len(all_channels)*C.xerrc) 1122 if cerr > abs(limit): 1123 to_refine.append(C) 1124 else: 1125 rerr += cerr 1126 rerr *=rerr 1127 if not len(to_refine): 1128 return 1129 1130 # change limit since most don't contribute 1131 limit = math.sqrt((self.err_goal * xtot)**2 - rerr/math.sqrt(len(to_refine))) 1132 for C in to_refine[:]: 1133 cerr = C.mfactor*(C.xerru + len(to_refine)*C.xerrc) 1134 if cerr < limit: 1135 to_refine.remove(C) 1136 1137 # all the channel are now selected. create the channel information 1138 logger.info('need to improve %s channels' % len(to_refine)) 1139 1140 1141 jobs = [] # list of the refine if some job are split is list of 1142 # dict with the parameter of the run. 1143 1144 # loop over the channel to refine 1145 for C in to_refine: 1146 1147 #1. Determine how many events we need in each iteration 1148 yerr = C.mfactor*(C.xerru+len(to_refine)*C.xerrc) 1149 nevents = 0.2*C.nevents*(yerr/limit)**2 1150 1151 nb_split = int((nevents*(C.nunwgt/C.nevents)/self.max_request_event/ (2**self.min_iter-1))**(2/3)) 1152 nb_split = max(nb_split, 1) 1153 # **(2/3) to slow down the increase in number of jobs 1154 if nb_split > self.max_splitting: 1155 nb_split = self.max_splitting 1156 1157 if nb_split >1: 1158 nevents = nevents / nb_split 1159 self.write_multijob(C, nb_split) 1160 # forbid too low/too large value 1161 nevents = min(self.min_event_in_iter, max(self.max_event_in_iter, nevents)) 1162 1163 1164 #create the info dict assume no splitting for the default 1165 info = {'name': self.cmd.results.current['run_name'], 1166 'script_name': 'unknown', 1167 'directory': C.name, # need to be change for splitted job 1168 'P_dir': C.parent_name, 1169 'offset': 1, # need to be change for splitted job 1170 'nevents': nevents, 1171 'maxiter': self.max_iter, 1172 'miniter': self.min_iter, 1173 'precision': yerr/math.sqrt(nb_split)/(C.get('xsec')+ yerr), 1174 'nhel': self.run_card['nhel'], 1175 'channel': C.name.replace('G',''), 1176 'grid_refinment' : 1 1177 } 1178 1179 if nb_split == 1: 1180 jobs.append(info) 1181 else: 1182 for i in range(nb_split): 1183 new_info = dict(info) 1184 new_info['offset'] = i+1 1185 new_info['directory'] += self.alphabet[i % 26] + str((i+1)//26) 1186 jobs.append(new_info) 1187 self.create_ajob(pjoin(self.me_dir, 'SubProcesses', 'refine.sh'), jobs)
1188
1189 - def update_html(self):
1190 """update the html from this object since it contains all the information""" 1191 1192 1193 run = self.cmd.results.current['run_name'] 1194 if not os.path.exists(pjoin(self.cmd.me_dir, 'HTML', run)): 1195 os.mkdir(pjoin(self.cmd.me_dir, 'HTML', run)) 1196 1197 unit = self.cmd.results.unit 1198 P_text = "" 1199 if self.results: 1200 Presults = self.results 1201 else: 1202 self.results = sum_html.collect_result(self.cmd, None) 1203 Presults = self.results 1204 1205 for P_comb in Presults: 1206 P_text += P_comb.get_html(run, unit, self.cmd.me_dir) 1207 1208 Presults.write_results_dat(pjoin(self.cmd.me_dir,'SubProcesses', 'results.dat')) 1209 1210 fsock = open(pjoin(self.cmd.me_dir, 'HTML', run, 'results.html'),'w') 1211 fsock.write(sum_html.results_header) 1212 fsock.write('%s <dl>' % Presults.get_html(run, unit, self.cmd.me_dir)) 1213 fsock.write('%s </dl></body>' % P_text) 1214 1215 self.cmd.results.add_detail('cross', Presults.xsec) 1216 self.cmd.results.add_detail('error', Presults.xerru) 1217 1218 return Presults.xsec, Presults.xerru
1219
1220 1221 1222 1223 -class gen_ximprove_v4_nogridupdate(gen_ximprove_v4):
1224 1225 # some hardcoded value which impact the generation 1226 gen_events_security = 1.1 # multiply the number of requested event by this number for security 1227 combining_job = 0 # allow to run multiple channel in sequence 1228 max_request_event = 400 # split jobs if a channel if it needs more than that 1229 max_event_in_iter = 500 1230 min_event_in_iter = 250 1231 max_splitting = 260 # maximum duplication of a given channel 1232 min_iter = 2 1233 max_iter = 6 1234 keep_grid_for_refine = True 1235 1236
1237 - def __init__(self, cmd, opt=None):
1238 1239 gen_ximprove.__init__(cmd, opt) 1240 1241 if cmd.proc_characteristics['loopinduced'] and \ 1242 cmd.proc_characteristics['nexternal'] > 2: 1243 self.increase_parralelization(cmd.proc_characteristics['nexternal'])
1244
1245 - def increase_parralelization(self, nexternal):
1246 1247 self.max_splitting = 1000 1248 1249 if self.run_card['refine_evt_by_job'] != -1: 1250 pass 1251 elif nexternal == 3: 1252 self.max_request_event = 200 1253 elif nexternal == 4: 1254 self.max_request_event = 100 1255 elif nexternal >= 5: 1256 self.max_request_event = 50 1257 self.min_event_in_iter = 125 1258 self.max_iter = 5
1259
1260 -class gen_ximprove_share(gen_ximprove, gensym):
1261 """Doing the refine in multicore. Each core handle a couple of PS point.""" 1262 1263 nb_ps_by_job = 2000 1264 mode = "refine" 1265 gen_events_security = 1.15 1266 # Note the real security is lower since we stop the jobs if they are at 96% 1267 # of this target. 1268
1269 - def __init__(self, *args, **opts):
1270 1271 super(gen_ximprove_share, self).__init__(*args, **opts) 1272 self.generated_events = {} 1273 self.splitted_for_dir = lambda x,y : self.splitted_Pdir[(x,y)]
1274 1275
1276 - def get_job_for_event(self):
1277 """generate the script in order to generate a given number of event""" 1278 # correspond to write_gen in the fortran version 1279 1280 1281 goal_lum, to_refine = self.find_job_for_event() 1282 self.goal_lum = goal_lum 1283 1284 # loop over the channel to refine to find the number of PS point to launch 1285 total_ps_points = 0 1286 channel_to_ps_point = [] 1287 for C in to_refine: 1288 #0. remove previous events files 1289 try: 1290 os.remove(pjoin(self.me_dir, "SubProcesses",C.parent_name, C.name, "events.lhe")) 1291 except: 1292 pass 1293 1294 #1. Compute the number of points are needed to reach target 1295 needed_event = goal_lum*C.get('axsec') 1296 if needed_event == 0: 1297 continue 1298 #2. estimate how many points we need in each iteration 1299 if C.get('nunwgt') > 0: 1300 nevents = needed_event * (C.get('nevents') / C.get('nunwgt')) 1301 #split by iter 1302 nevents = int(nevents / (2**self.min_iter-1)) 1303 else: 1304 nb_split = int(max(1,((needed_event-1)// self.max_request_event) +1)) 1305 if not self.split_channels: 1306 nb_split = 1 1307 if nb_split > self.max_splitting: 1308 nb_split = self.max_splitting 1309 nevents = self.max_event_in_iter * self.max_splitting 1310 else: 1311 nevents = self.max_event_in_iter * nb_split 1312 1313 if nevents > self.max_splitting*self.max_event_in_iter: 1314 logger.warning("Channel %s/%s has a very low efficiency of unweighting. Might not be possible to reach target" % \ 1315 (C.name, C.parent_name)) 1316 nevents = self.max_event_in_iter * self.max_splitting 1317 1318 total_ps_points += nevents 1319 channel_to_ps_point.append((C, nevents)) 1320 1321 if self.cmd.options["run_mode"] == 1: 1322 if self.cmd.options["cluster_size"]: 1323 nb_ps_by_job = total_ps_points /int(self.cmd.options["cluster_size"]) 1324 else: 1325 nb_ps_by_job = self.nb_ps_by_job 1326 elif self.cmd.options["run_mode"] == 2: 1327 remain = total_ps_points % self.cmd.options["nb_core"] 1328 if remain: 1329 nb_ps_by_job = 1 + (total_ps_points - remain) / self.cmd.options["nb_core"] 1330 else: 1331 nb_ps_by_job = total_ps_points / self.cmd.options["nb_core"] 1332 else: 1333 nb_ps_by_job = self.nb_ps_by_job 1334 1335 nb_ps_by_job = int(max(nb_ps_by_job, 500)) 1336 1337 for C, nevents in channel_to_ps_point: 1338 if nevents % nb_ps_by_job: 1339 nb_job = 1 + int(nevents // nb_ps_by_job) 1340 else: 1341 nb_job = int(nevents // nb_ps_by_job) 1342 submit_ps = min(nevents, nb_ps_by_job) 1343 if nb_job == 1: 1344 submit_ps = max(submit_ps, self.min_event_in_iter) 1345 self.create_resubmit_one_iter(C.parent_name, C.name[1:], submit_ps, nb_job, step=0) 1346 needed_event = goal_lum*C.get('xsec') 1347 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)
1348 1349
1350 - def combine_iteration(self, Pdir, G, step):
1351 1352 grid_calculator, cross, error = self.combine_grid(Pdir, G, step) 1353 1354 # collect all the generated_event 1355 Gdirs = [] #build the the list of directory 1356 for i in range(self.splitted_for_dir(Pdir, G)): 1357 path = pjoin(Pdir, "G%s_%s" % (G, i+1)) 1358 Gdirs.append(path) 1359 assert len(grid_calculator.results) == len(Gdirs) == self.splitted_for_dir(Pdir, G) 1360 1361 1362 # Check how many events are going to be kept after un-weighting. 1363 needed_event = cross * self.goal_lum 1364 if needed_event == 0: 1365 return 0 1366 # check that the number of events requested is not higher than the actual 1367 # total number of events to generate. 1368 if self.err_goal >=1: 1369 if needed_event > self.gen_events_security * self.err_goal: 1370 needed_event = int(self.gen_events_security * self.err_goal) 1371 1372 if (Pdir, G) in self.generated_events: 1373 old_nunwgt, old_maxwgt = self.generated_events[(Pdir, G)] 1374 else: 1375 old_nunwgt, old_maxwgt = 0, 0 1376 1377 if old_nunwgt == 0 and os.path.exists(pjoin(Pdir,"G%s" % G, "events.lhe")): 1378 # possible for second refine. 1379 lhe = lhe_parser.EventFile(pjoin(Pdir,"G%s" % G, "events.lhe")) 1380 old_nunwgt = lhe.unweight(None, trunc_error=0.005, log_level=0) 1381 old_maxwgt = lhe.max_wgt 1382 1383 1384 1385 maxwgt = max(grid_calculator.get_max_wgt(), old_maxwgt) 1386 new_evt = grid_calculator.get_nunwgt(maxwgt) 1387 efficiency = new_evt / sum([R.nevents for R in grid_calculator.results]) 1388 nunwgt = old_nunwgt * old_maxwgt / maxwgt 1389 nunwgt += new_evt 1390 1391 # check the number of event for this iteration alone 1392 one_iter_nb_event = grid_calculator.get_nunwgt() 1393 drop_previous_iteration = False 1394 # compare the number of events to generate if we discard the previous iteration 1395 n_target_one_iter = (needed_event-one_iter_nb_event) / ( one_iter_nb_event/ sum([R.nevents for R in grid_calculator.results])) 1396 n_target_combined = (needed_event-nunwgt) / efficiency 1397 if n_target_one_iter < n_target_combined: 1398 # the last iteration alone has more event that the combine iteration. 1399 # it is therefore interesting to drop previous iteration. 1400 drop_previous_iteration = True 1401 nunwgt = one_iter_nb_event 1402 maxwgt = grid_calculator.get_max_wgt() 1403 new_evt = nunwgt 1404 efficiency = ( one_iter_nb_event/ sum([R.nevents for R in grid_calculator.results])) 1405 1406 try: 1407 if drop_previous_iteration: 1408 raise IOError 1409 output_file = open(pjoin(Pdir,"G%s" % G, "events.lhe"), 'a') 1410 except IOError: 1411 output_file = open(pjoin(Pdir,"G%s" % G, "events.lhe"), 'w') 1412 1413 misc.call(["cat"] + [pjoin(d, "events.lhe") for d in Gdirs], 1414 stdout=output_file) 1415 output_file.close() 1416 # For large number of iteration. check the number of event by doing the 1417 # real unweighting. 1418 if nunwgt < 0.6 * needed_event and step > self.min_iter: 1419 lhe = lhe_parser.EventFile(output_file.name) 1420 old_nunwgt =nunwgt 1421 nunwgt = lhe.unweight(None, trunc_error=0.01, log_level=0) 1422 1423 1424 self.generated_events[(Pdir, G)] = (nunwgt, maxwgt) 1425 1426 # misc.sprint("Adding %s event to %s. Currently at %s" % (new_evt, G, nunwgt)) 1427 # check what to do 1428 if nunwgt >= int(0.96*needed_event)+1: # 0.96*1.15=1.10 =real security 1429 # We did it. 1430 logger.info("found enough event for %s/G%s" % (os.path.basename(Pdir), G)) 1431 self.write_results(grid_calculator, cross, error, Pdir, G, step, efficiency) 1432 return 0 1433 elif step >= self.max_iter: 1434 logger.debug("fail to find enough event") 1435 self.write_results(grid_calculator, cross, error, Pdir, G, step, efficiency) 1436 return 0 1437 1438 nb_split_before = len(grid_calculator.results) 1439 nevents = grid_calculator.results[0].nevents 1440 if nevents == 0: # possible if some integral returns 0 1441 nevents = max(g.nevents for g in grid_calculator.results) 1442 1443 need_ps_point = (needed_event - nunwgt)/(efficiency+1e-99) 1444 need_job = need_ps_point // nevents + 1 1445 1446 if step < self.min_iter: 1447 # This is normal but check if we are on the good track 1448 job_at_first_iter = nb_split_before/2**(step-1) 1449 expected_total_job = job_at_first_iter * (2**self.min_iter-1) 1450 done_job = job_at_first_iter * (2**step-1) 1451 expected_remaining_job = expected_total_job - done_job 1452 1453 logger.debug("efficiency status (smaller is better): %s", need_job/expected_remaining_job) 1454 # increase if needed but not too much 1455 need_job = min(need_job, expected_remaining_job*1.25) 1456 1457 nb_job = (need_job-0.5)//(2**(self.min_iter-step)-1) + 1 1458 nb_job = max(1, nb_job) 1459 grid_calculator.write_grid_for_submission(Pdir,G, 1460 self.splitted_for_dir(Pdir, G), nb_job*nevents ,mode=self.mode, 1461 conservative_factor=self.max_iter) 1462 logger.info("%s/G%s is at %i/%i (%.2g%%) event. Resubmit %i job at iteration %i." \ 1463 % (os.path.basename(Pdir), G, int(nunwgt),int(needed_event)+1, 1464 (float(nunwgt)/needed_event)*100.0 if needed_event>0.0 else 0.0, 1465 nb_job, step)) 1466 self.create_resubmit_one_iter(Pdir, G, nevents, nb_job, step) 1467 #self.create_job(Pdir, G, nb_job, nevents, step) 1468 1469 elif step < self.max_iter: 1470 if step + 1 == self.max_iter: 1471 need_job = 1.20 * need_job # avoid to have just too few event. 1472 1473 nb_job = int(min(need_job, nb_split_before*1.5)) 1474 grid_calculator.write_grid_for_submission(Pdir,G, 1475 self.splitted_for_dir(Pdir, G), nb_job*nevents ,mode=self.mode, 1476 conservative_factor=self.max_iter) 1477 1478 1479 logger.info("%s/G%s is at %i/%i ('%.2g%%') event. Resubmit %i job at iteration %i." \ 1480 % (os.path.basename(Pdir), G, int(nunwgt),int(needed_event)+1, 1481 (float(nunwgt)/needed_event)*100.0 if needed_event>0.0 else 0.0, 1482 nb_job, step)) 1483 self.create_resubmit_one_iter(Pdir, G, nevents, nb_job, step) 1484 1485 1486 1487 return 0
1488 1489
1490 - def write_results(self, grid_calculator, cross, error, Pdir, G, step, efficiency):
1491 1492 #compute the value 1493 if cross == 0: 1494 abscross,nw, luminosity = 0, 0, 0 1495 wgt, maxit,nunwgt, wgt, nevents = 0,0,0,0,0 1496 error = 0 1497 else: 1498 grid_calculator.results.compute_values() 1499 abscross = self.abscross[(Pdir,G)]/self.sigma[(Pdir,G)] 1500 nunwgt, wgt = self.generated_events[(Pdir, G)] 1501 nw = int(nunwgt / efficiency) 1502 nunwgt = int(nunwgt) 1503 maxit = step 1504 nevents = nunwgt 1505 # make the unweighting to compute the number of events: 1506 luminosity = nunwgt/cross 1507 1508 #format the results.dat 1509 def fstr(nb): 1510 data = '%E' % nb 1511 nb, power = data.split('E') 1512 nb = float(nb) /10 1513 power = int(power) + 1 1514 return '%.5fE%+03i' %(nb,power)
1515 line = '%s %s %s %i %i %i %i %s %s %s 0.0 0.0 0\n' % \ 1516 (fstr(cross), fstr(error*cross), fstr(error*cross), 1517 nevents, nw, maxit,nunwgt, 1518 fstr(luminosity), fstr(wgt), fstr(abscross)) 1519 1520 fsock = open(pjoin(self.me_dir,'SubProcesses' , Pdir, 'G%s' % G, 1521 'results.dat'),'w') 1522 fsock.writelines(line) 1523 fsock.close()
1524