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 if "base_directory" not in info: 1104 info["base_directory"] = "./" 1105 fsock.write(template_text % info) 1106 nb_use += nb_job
1107
1108 - def get_job_for_precision(self):
1109 """create the ajob to achieve a give precision on the total cross-section""" 1110 1111 1112 assert self.err_goal <=1 1113 xtot = abs(self.results.xsec) 1114 logger.info("Working on precision: %s %%" %(100*self.err_goal)) 1115 all_channels = sum([list(P) for P in self.results if P.mfactor],[]) 1116 limit = self.err_goal * xtot / len(all_channels) 1117 to_refine = [] 1118 rerr = 0 #error of the job not directly selected 1119 for C in all_channels: 1120 cerr = C.mfactor*(C.xerru + len(all_channels)*C.xerrc) 1121 if cerr > abs(limit): 1122 to_refine.append(C) 1123 else: 1124 rerr += cerr 1125 rerr *=rerr 1126 if not len(to_refine): 1127 return 1128 1129 # change limit since most don't contribute 1130 limit = math.sqrt((self.err_goal * xtot)**2 - rerr/math.sqrt(len(to_refine))) 1131 for C in to_refine[:]: 1132 cerr = C.mfactor*(C.xerru + len(to_refine)*C.xerrc) 1133 if cerr < limit: 1134 to_refine.remove(C) 1135 1136 # all the channel are now selected. create the channel information 1137 logger.info('need to improve %s channels' % len(to_refine)) 1138 1139 1140 jobs = [] # list of the refine if some job are split is list of 1141 # dict with the parameter of the run. 1142 1143 # loop over the channel to refine 1144 for C in to_refine: 1145 1146 #1. Determine how many events we need in each iteration 1147 yerr = C.mfactor*(C.xerru+len(to_refine)*C.xerrc) 1148 nevents = 0.2*C.nevents*(yerr/limit)**2 1149 1150 nb_split = int((nevents*(C.nunwgt/C.nevents)/self.max_request_event/ (2**self.min_iter-1))**(2/3)) 1151 nb_split = max(nb_split, 1) 1152 # **(2/3) to slow down the increase in number of jobs 1153 if nb_split > self.max_splitting: 1154 nb_split = self.max_splitting 1155 1156 if nb_split >1: 1157 nevents = nevents / nb_split 1158 self.write_multijob(C, nb_split) 1159 # forbid too low/too large value 1160 nevents = min(self.min_event_in_iter, max(self.max_event_in_iter, nevents)) 1161 1162 1163 #create the info dict assume no splitting for the default 1164 info = {'name': self.cmd.results.current['run_name'], 1165 'script_name': 'unknown', 1166 'directory': C.name, # need to be change for splitted job 1167 'P_dir': C.parent_name, 1168 'offset': 1, # need to be change for splitted job 1169 'nevents': nevents, 1170 'maxiter': self.max_iter, 1171 'miniter': self.min_iter, 1172 'precision': yerr/math.sqrt(nb_split)/(C.get('xsec')+ yerr), 1173 'nhel': self.run_card['nhel'], 1174 'channel': C.name.replace('G',''), 1175 'grid_refinment' : 1 1176 } 1177 1178 if nb_split == 1: 1179 jobs.append(info) 1180 else: 1181 for i in range(nb_split): 1182 new_info = dict(info) 1183 new_info['offset'] = i+1 1184 new_info['directory'] += self.alphabet[i % 26] + str((i+1)//26) 1185 jobs.append(new_info) 1186 self.create_ajob(pjoin(self.me_dir, 'SubProcesses', 'refine.sh'), jobs)
1187
1188 - def update_html(self):
1189 """update the html from this object since it contains all the information""" 1190 1191 1192 run = self.cmd.results.current['run_name'] 1193 if not os.path.exists(pjoin(self.cmd.me_dir, 'HTML', run)): 1194 os.mkdir(pjoin(self.cmd.me_dir, 'HTML', run)) 1195 1196 unit = self.cmd.results.unit 1197 P_text = "" 1198 if self.results: 1199 Presults = self.results 1200 else: 1201 self.results = sum_html.collect_result(self.cmd, None) 1202 Presults = self.results 1203 1204 for P_comb in Presults: 1205 P_text += P_comb.get_html(run, unit, self.cmd.me_dir) 1206 1207 Presults.write_results_dat(pjoin(self.cmd.me_dir,'SubProcesses', 'results.dat')) 1208 1209 fsock = open(pjoin(self.cmd.me_dir, 'HTML', run, 'results.html'),'w') 1210 fsock.write(sum_html.results_header) 1211 fsock.write('%s <dl>' % Presults.get_html(run, unit, self.cmd.me_dir)) 1212 fsock.write('%s </dl></body>' % P_text) 1213 1214 self.cmd.results.add_detail('cross', Presults.xsec) 1215 self.cmd.results.add_detail('error', Presults.xerru) 1216 1217 return Presults.xsec, Presults.xerru
1218
1219 1220 1221 1222 -class gen_ximprove_v4_nogridupdate(gen_ximprove_v4):
1223 1224 # some hardcoded value which impact the generation 1225 gen_events_security = 1.1 # multiply the number of requested event by this number for security 1226 combining_job = 0 # allow to run multiple channel in sequence 1227 max_request_event = 400 # split jobs if a channel if it needs more than that 1228 max_event_in_iter = 500 1229 min_event_in_iter = 250 1230 max_splitting = 260 # maximum duplication of a given channel 1231 min_iter = 2 1232 max_iter = 6 1233 keep_grid_for_refine = True 1234 1235
1236 - def __init__(self, cmd, opt=None):
1237 1238 gen_ximprove.__init__(cmd, opt) 1239 1240 if cmd.proc_characteristics['loopinduced'] and \ 1241 cmd.proc_characteristics['nexternal'] > 2: 1242 self.increase_parralelization(cmd.proc_characteristics['nexternal'])
1243
1244 - def increase_parralelization(self, nexternal):
1245 1246 self.max_splitting = 1000 1247 1248 if self.run_card['refine_evt_by_job'] != -1: 1249 pass 1250 elif nexternal == 3: 1251 self.max_request_event = 200 1252 elif nexternal == 4: 1253 self.max_request_event = 100 1254 elif nexternal >= 5: 1255 self.max_request_event = 50 1256 self.min_event_in_iter = 125 1257 self.max_iter = 5
1258
1259 -class gen_ximprove_share(gen_ximprove, gensym):
1260 """Doing the refine in multicore. Each core handle a couple of PS point.""" 1261 1262 nb_ps_by_job = 2000 1263 mode = "refine" 1264 gen_events_security = 1.15 1265 # Note the real security is lower since we stop the jobs if they are at 96% 1266 # of this target. 1267
1268 - def __init__(self, *args, **opts):
1269 1270 super(gen_ximprove_share, self).__init__(*args, **opts) 1271 self.generated_events = {} 1272 self.splitted_for_dir = lambda x,y : self.splitted_Pdir[(x,y)]
1273 1274
1275 - def get_job_for_event(self):
1276 """generate the script in order to generate a given number of event""" 1277 # correspond to write_gen in the fortran version 1278 1279 1280 goal_lum, to_refine = self.find_job_for_event() 1281 self.goal_lum = goal_lum 1282 1283 # loop over the channel to refine to find the number of PS point to launch 1284 total_ps_points = 0 1285 channel_to_ps_point = [] 1286 for C in to_refine: 1287 #0. remove previous events files 1288 try: 1289 os.remove(pjoin(self.me_dir, "SubProcesses",C.parent_name, C.name, "events.lhe")) 1290 except: 1291 pass 1292 1293 #1. Compute the number of points are needed to reach target 1294 needed_event = goal_lum*C.get('axsec') 1295 if needed_event == 0: 1296 continue 1297 #2. estimate how many points we need in each iteration 1298 if C.get('nunwgt') > 0: 1299 nevents = needed_event * (C.get('nevents') / C.get('nunwgt')) 1300 #split by iter 1301 nevents = int(nevents / (2**self.min_iter-1)) 1302 else: 1303 nb_split = int(max(1,((needed_event-1)// self.max_request_event) +1)) 1304 if not self.split_channels: 1305 nb_split = 1 1306 if nb_split > self.max_splitting: 1307 nb_split = self.max_splitting 1308 nevents = self.max_event_in_iter * self.max_splitting 1309 else: 1310 nevents = self.max_event_in_iter * nb_split 1311 1312 if nevents > self.max_splitting*self.max_event_in_iter: 1313 logger.warning("Channel %s/%s has a very low efficiency of unweighting. Might not be possible to reach target" % \ 1314 (C.name, C.parent_name)) 1315 nevents = self.max_event_in_iter * self.max_splitting 1316 1317 total_ps_points += nevents 1318 channel_to_ps_point.append((C, nevents)) 1319 1320 if self.cmd.options["run_mode"] == 1: 1321 if self.cmd.options["cluster_size"]: 1322 nb_ps_by_job = total_ps_points /int(self.cmd.options["cluster_size"]) 1323 else: 1324 nb_ps_by_job = self.nb_ps_by_job 1325 elif self.cmd.options["run_mode"] == 2: 1326 remain = total_ps_points % self.cmd.options["nb_core"] 1327 if remain: 1328 nb_ps_by_job = 1 + (total_ps_points - remain) / self.cmd.options["nb_core"] 1329 else: 1330 nb_ps_by_job = total_ps_points / self.cmd.options["nb_core"] 1331 else: 1332 nb_ps_by_job = self.nb_ps_by_job 1333 1334 nb_ps_by_job = int(max(nb_ps_by_job, 500)) 1335 1336 for C, nevents in channel_to_ps_point: 1337 if nevents % nb_ps_by_job: 1338 nb_job = 1 + int(nevents // nb_ps_by_job) 1339 else: 1340 nb_job = int(nevents // nb_ps_by_job) 1341 submit_ps = min(nevents, nb_ps_by_job) 1342 if nb_job == 1: 1343 submit_ps = max(submit_ps, self.min_event_in_iter) 1344 self.create_resubmit_one_iter(C.parent_name, C.name[1:], submit_ps, nb_job, step=0) 1345 needed_event = goal_lum*C.get('xsec') 1346 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)
1347 1348
1349 - def combine_iteration(self, Pdir, G, step):
1350 1351 grid_calculator, cross, error = self.combine_grid(Pdir, G, step) 1352 1353 # collect all the generated_event 1354 Gdirs = [] #build the the list of directory 1355 for i in range(self.splitted_for_dir(Pdir, G)): 1356 path = pjoin(Pdir, "G%s_%s" % (G, i+1)) 1357 Gdirs.append(path) 1358 assert len(grid_calculator.results) == len(Gdirs) == self.splitted_for_dir(Pdir, G) 1359 1360 1361 # Check how many events are going to be kept after un-weighting. 1362 needed_event = cross * self.goal_lum 1363 if needed_event == 0: 1364 return 0 1365 # check that the number of events requested is not higher than the actual 1366 # total number of events to generate. 1367 if self.err_goal >=1: 1368 if needed_event > self.gen_events_security * self.err_goal: 1369 needed_event = int(self.gen_events_security * self.err_goal) 1370 1371 if (Pdir, G) in self.generated_events: 1372 old_nunwgt, old_maxwgt = self.generated_events[(Pdir, G)] 1373 else: 1374 old_nunwgt, old_maxwgt = 0, 0 1375 1376 if old_nunwgt == 0 and os.path.exists(pjoin(Pdir,"G%s" % G, "events.lhe")): 1377 # possible for second refine. 1378 lhe = lhe_parser.EventFile(pjoin(Pdir,"G%s" % G, "events.lhe")) 1379 old_nunwgt = lhe.unweight(None, trunc_error=0.005, log_level=0) 1380 old_maxwgt = lhe.max_wgt 1381 1382 1383 1384 maxwgt = max(grid_calculator.get_max_wgt(), old_maxwgt) 1385 new_evt = grid_calculator.get_nunwgt(maxwgt) 1386 efficiency = new_evt / sum([R.nevents for R in grid_calculator.results]) 1387 nunwgt = old_nunwgt * old_maxwgt / maxwgt 1388 nunwgt += new_evt 1389 1390 # check the number of event for this iteration alone 1391 one_iter_nb_event = grid_calculator.get_nunwgt() 1392 drop_previous_iteration = False 1393 # compare the number of events to generate if we discard the previous iteration 1394 n_target_one_iter = (needed_event-one_iter_nb_event) / ( one_iter_nb_event/ sum([R.nevents for R in grid_calculator.results])) 1395 n_target_combined = (needed_event-nunwgt) / efficiency 1396 if n_target_one_iter < n_target_combined: 1397 # the last iteration alone has more event that the combine iteration. 1398 # it is therefore interesting to drop previous iteration. 1399 drop_previous_iteration = True 1400 nunwgt = one_iter_nb_event 1401 maxwgt = grid_calculator.get_max_wgt() 1402 new_evt = nunwgt 1403 efficiency = ( one_iter_nb_event/ sum([R.nevents for R in grid_calculator.results])) 1404 1405 try: 1406 if drop_previous_iteration: 1407 raise IOError 1408 output_file = open(pjoin(Pdir,"G%s" % G, "events.lhe"), 'a') 1409 except IOError: 1410 output_file = open(pjoin(Pdir,"G%s" % G, "events.lhe"), 'w') 1411 1412 misc.call(["cat"] + [pjoin(d, "events.lhe") for d in Gdirs], 1413 stdout=output_file) 1414 output_file.close() 1415 # For large number of iteration. check the number of event by doing the 1416 # real unweighting. 1417 if nunwgt < 0.6 * needed_event and step > self.min_iter: 1418 lhe = lhe_parser.EventFile(output_file.name) 1419 old_nunwgt =nunwgt 1420 nunwgt = lhe.unweight(None, trunc_error=0.01, log_level=0) 1421 1422 1423 self.generated_events[(Pdir, G)] = (nunwgt, maxwgt) 1424 1425 # misc.sprint("Adding %s event to %s. Currently at %s" % (new_evt, G, nunwgt)) 1426 # check what to do 1427 if nunwgt >= int(0.96*needed_event)+1: # 0.96*1.15=1.10 =real security 1428 # We did it. 1429 logger.info("found enough event for %s/G%s" % (os.path.basename(Pdir), G)) 1430 self.write_results(grid_calculator, cross, error, Pdir, G, step, efficiency) 1431 return 0 1432 elif step >= self.max_iter: 1433 logger.debug("fail to find enough event") 1434 self.write_results(grid_calculator, cross, error, Pdir, G, step, efficiency) 1435 return 0 1436 1437 nb_split_before = len(grid_calculator.results) 1438 nevents = grid_calculator.results[0].nevents 1439 if nevents == 0: # possible if some integral returns 0 1440 nevents = max(g.nevents for g in grid_calculator.results) 1441 1442 need_ps_point = (needed_event - nunwgt)/(efficiency+1e-99) 1443 need_job = need_ps_point // nevents + 1 1444 1445 if step < self.min_iter: 1446 # This is normal but check if we are on the good track 1447 job_at_first_iter = nb_split_before/2**(step-1) 1448 expected_total_job = job_at_first_iter * (2**self.min_iter-1) 1449 done_job = job_at_first_iter * (2**step-1) 1450 expected_remaining_job = expected_total_job - done_job 1451 1452 logger.debug("efficiency status (smaller is better): %s", need_job/expected_remaining_job) 1453 # increase if needed but not too much 1454 need_job = min(need_job, expected_remaining_job*1.25) 1455 1456 nb_job = (need_job-0.5)//(2**(self.min_iter-step)-1) + 1 1457 nb_job = max(1, nb_job) 1458 grid_calculator.write_grid_for_submission(Pdir,G, 1459 self.splitted_for_dir(Pdir, G), nb_job*nevents ,mode=self.mode, 1460 conservative_factor=self.max_iter) 1461 logger.info("%s/G%s is at %i/%i (%.2g%%) event. Resubmit %i job at iteration %i." \ 1462 % (os.path.basename(Pdir), G, int(nunwgt),int(needed_event)+1, 1463 (float(nunwgt)/needed_event)*100.0 if needed_event>0.0 else 0.0, 1464 nb_job, step)) 1465 self.create_resubmit_one_iter(Pdir, G, nevents, nb_job, step) 1466 #self.create_job(Pdir, G, nb_job, nevents, step) 1467 1468 elif step < self.max_iter: 1469 if step + 1 == self.max_iter: 1470 need_job = 1.20 * need_job # avoid to have just too few event. 1471 1472 nb_job = int(min(need_job, nb_split_before*1.5)) 1473 grid_calculator.write_grid_for_submission(Pdir,G, 1474 self.splitted_for_dir(Pdir, G), nb_job*nevents ,mode=self.mode, 1475 conservative_factor=self.max_iter) 1476 1477 1478 logger.info("%s/G%s is at %i/%i ('%.2g%%') event. Resubmit %i job at iteration %i." \ 1479 % (os.path.basename(Pdir), G, int(nunwgt),int(needed_event)+1, 1480 (float(nunwgt)/needed_event)*100.0 if needed_event>0.0 else 0.0, 1481 nb_job, step)) 1482 self.create_resubmit_one_iter(Pdir, G, nevents, nb_job, step) 1483 1484 1485 1486 return 0
1487 1488
1489 - def write_results(self, grid_calculator, cross, error, Pdir, G, step, efficiency):
1490 1491 #compute the value 1492 if cross == 0: 1493 abscross,nw, luminosity = 0, 0, 0 1494 wgt, maxit,nunwgt, wgt, nevents = 0,0,0,0,0 1495 error = 0 1496 else: 1497 grid_calculator.results.compute_values() 1498 abscross = self.abscross[(Pdir,G)]/self.sigma[(Pdir,G)] 1499 nunwgt, wgt = self.generated_events[(Pdir, G)] 1500 nw = int(nunwgt / efficiency) 1501 nunwgt = int(nunwgt) 1502 maxit = step 1503 nevents = nunwgt 1504 # make the unweighting to compute the number of events: 1505 luminosity = nunwgt/cross 1506 1507 #format the results.dat 1508 def fstr(nb): 1509 data = '%E' % nb 1510 nb, power = data.split('E') 1511 nb = float(nb) /10 1512 power = int(power) + 1 1513 return '%.5fE%+03i' %(nb,power)
1514 line = '%s %s %s %i %i %i %i %s %s %s 0.0 0.0 0\n' % \ 1515 (fstr(cross), fstr(error*cross), fstr(error*cross), 1516 nevents, nw, maxit,nunwgt, 1517 fstr(luminosity), fstr(wgt), fstr(abscross)) 1518 1519 fsock = open(pjoin(self.me_dir,'SubProcesses' , Pdir, 'G%s' % G, 1520 'results.dat'),'w') 1521 fsock.writelines(line) 1522 fsock.close()
1523