################################################################################
#
# Copyright (c) 2014 The MadGraph5_aMC@NLO Development team and Contributors
#
# This file is a part of the MadGraph5_aMC@NLO project, an application which
# automatically generates Feynman diagrams and matrix elements for arbitrary
# high-energy processes in the Standard Model and beyond.
#
# It is subject to the MadGraph5_aMC@NLO license which should accompany this
# distribution.
#
# For more information, visit madgraph.phys.ucl.ac.be and amcatnlo.web.cern.ch
#
################################################################################
""" A python file to replace the fortran script gen_ximprove.
This script analyses the result of the survey/ previous refine and
creates the jobs for the following script.
"""
from __future__ import division
import collections
import os
import glob
import logging
import math
import re
import subprocess
import shutil
try:
import madgraph
except ImportError:
MADEVENT = True
import internal.sum_html as sum_html
import internal.banner as bannermod
import internal.misc as misc
import internal.files as files
import internal.cluster as cluster
import internal.combine_grid as combine_grid
import internal.combine_runs as combine_runs
import internal.lhe_parser as lhe_parser
else:
MADEVENT= False
import madgraph.madevent.sum_html as sum_html
import madgraph.various.banner as bannermod
import madgraph.various.misc as misc
import madgraph.iolibs.files as files
import madgraph.various.cluster as cluster
import madgraph.madevent.combine_grid as combine_grid
import madgraph.madevent.combine_runs as combine_runs
import madgraph.various.lhe_parser as lhe_parser
logger = logging.getLogger('madgraph.madevent.gen_ximprove')
pjoin = os.path.join
class gensym(object):
"""a class to call the fortran gensym executable and handle it's output
in order to create the various job that are needed for the survey"""
#convenient shortcut for the formatting of variable
@ staticmethod
def format_variable(*args):
return bannermod.ConfigFile.format_variable(*args)
combining_job = 2 # number of channel by ajob
splitted_grid = False
min_iterations = 3
mode= "survey"
def __init__(self, cmd, opt=None):
try:
super(gensym, self).__init__(cmd, opt)
except TypeError:
pass
# Run statistics, a dictionary of RunStatistics(), with
self.run_statistics = {}
self.cmd = cmd
self.run_card = cmd.run_card
self.me_dir = cmd.me_dir
# dictionary to keep track of the precision when combining iteration
self.cross = collections.defaultdict(int)
self.abscross = collections.defaultdict(int)
self.sigma = collections.defaultdict(int)
self.chi2 = collections.defaultdict(int)
self.splitted_grid = False
if self.cmd.proc_characteristics['loop_induced']:
nexternal = self.cmd.proc_characteristics['nexternal']
self.splitted_grid = max(2, (nexternal-2)**2)
if hasattr(self.cmd, "opts") and self.cmd.opts['accuracy'] == 0.1:
self.cmd.opts['accuracy'] = 0.02
if isinstance(cmd.cluster, cluster.MultiCore) and self.splitted_grid > 1:
self.splitted_grid = int(cmd.cluster.nb_core**0.5)
if self.splitted_grid == 1 and cmd.cluster.nb_core >1:
self.splitted_grid = 2
#if the user defines it in the run_card:
if self.run_card['survey_splitting'] != -1:
self.splitted_grid = self.run_card['survey_splitting']
self.splitted_Pdir = {}
self.splitted_for_dir = lambda x,y: self.splitted_grid
self.combining_job_for_Pdir = lambda x: self.combining_job
self.lastoffset = {}
def launch(self):
""" """
self.subproc = [l.strip() for l in open(pjoin(self.me_dir,'SubProcesses',
'subproc.mg'))]
subproc = self.subproc
P_zero_result = [] # check the number of times where they are no phase-space
nb_tot_proc = len(subproc)
for nb_proc,subdir in enumerate(subproc):
job_list = {}
self.cmd.update_status('Compiling for process %s/%s.
(previous processes already running)' % \
(nb_proc+1,nb_tot_proc), level=None)
subdir = subdir.strip()
Pdir = pjoin(self.me_dir, 'SubProcesses',subdir)
logger.info(' %s ' % subdir)
# clean previous run
for match in misc.glob('*ajob*', Pdir):
if os.path.basename(match)[:4] in ['ajob', 'wait', 'run.', 'done']:
os.remove(match)
for match in misc.glob('G*', Pdir):
if os.path.exists(pjoin(match,'results.dat')):
os.remove(pjoin(match, 'results.dat'))
if os.path.exists(pjoin(match, 'ftn25')):
os.remove(pjoin(match, 'ftn25'))
#compile gensym
self.cmd.compile(['gensym'], cwd=Pdir)
if not os.path.exists(pjoin(Pdir, 'gensym')):
raise Exception, 'Error make gensym not successful'
# Launch gensym
p = misc.Popen(['./gensym'], stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, cwd=Pdir)
#sym_input = "%(points)d %(iterations)d %(accuracy)f \n" % self.opts
(stdout, _) = p.communicate('')
if os.path.exists(pjoin(self.me_dir,'error')):
files.mv(pjoin(self.me_dir,'error'), pjoin(Pdir,'ajob.no_ps.log'))
P_zero_result.append(subdir)
continue
jobs = stdout.split()
job_list[Pdir] = jobs
try:
# check that all input are valid
[float(s) for s in jobs]
except Exception:
logger.debug("unformated string found in gensym. Please check:\n %s" % stdout)
done=False
job_list[Pdir] = []
lines = stdout.split('\n')
for l in lines:
try:
[float(s) for s in l.split()]
except:
continue
else:
if done:
raise Exception, 'Parsing error in gensym: %s' % stdout
job_list[Pdir] = l.split()
done = True
if not done:
raise Exception, 'Parsing error in gensym: %s' % stdout
self.cmd.compile(['madevent'], cwd=Pdir)
self.submit_to_cluster(job_list)
return job_list, P_zero_result
def submit_to_cluster(self, job_list):
""" """
if self.run_card['job_strategy'] > 0:
if len(job_list) >1:
for path, dirs in job_list.items():
self.submit_to_cluster({path:dirs})
return
path, value = job_list.items()[0]
nexternal = self.cmd.proc_characteristics['nexternal']
current = open(pjoin(path, "nexternal.inc")).read()
ext = re.search(r"PARAMETER \(NEXTERNAL=(\d+)\)", current).group(1)
if self.run_card['job_strategy'] == 2:
self.splitted_grid = 2
if nexternal == int(ext):
to_split = 2
else:
to_split = 0
if hasattr(self, 'splitted_Pdir'):
self.splitted_Pdir[path] = to_split
else:
self.splitted_Pdir = {path: to_split}
self.splitted_for_dir = lambda x,y : self.splitted_Pdir[x]
elif self.run_card['job_strategy'] == 1:
if nexternal == int(ext):
combine = 1
else:
combine = self.combining_job
if hasattr(self, 'splitted_Pdir'):
self.splitted_Pdir[path] = combine
else:
self.splitted_Pdir = {path: combine}
self.combining_job_for_Pdir = lambda x : self.splitted_Pdir[x]
if not self.splitted_grid:
return self.submit_to_cluster_no_splitting(job_list)
elif self.cmd.cluster_mode == 0:
return self.submit_to_cluster_no_splitting(job_list)
elif self.cmd.cluster_mode == 2 and self.cmd.options['nb_core'] == 1:
return self.submit_to_cluster_no_splitting(job_list)
else:
return self.submit_to_cluster_splitted(job_list)
def submit_to_cluster_no_splitting(self, job_list):
"""submit the survey without the parralelization.
This is the old mode which is still usefull in single core"""
# write the template file for the parameter file
self.write_parameter(parralelization=False, Pdirs=job_list.keys())
# launch the job with the appropriate grouping
for Pdir, jobs in job_list.items():
jobs = list(jobs)
i=0
while jobs:
i+=1
to_submit = ['0'] # the first entry is actually the offset
for _ in range(self.combining_job_for_Pdir(Pdir)):
if jobs:
to_submit.append(jobs.pop(0))
self.cmd.launch_job(pjoin(self.me_dir, 'SubProcesses', 'survey.sh'),
argument=to_submit,
cwd=pjoin(self.me_dir,'SubProcesses' , Pdir))
def create_resubmit_one_iter(self, Pdir, G, submit_ps, nb_job, step=0):
"""prepare the input_file for submitting the channel"""
if 'SubProcesses' not in Pdir:
Pdir = pjoin(self.me_dir, 'SubProcesses', Pdir)
#keep track of how many job are sended
self.splitted_Pdir[(Pdir, G)] = int(nb_job)
# 1. write the new input_app.txt
run_card = self.cmd.run_card
options = {'event' : submit_ps,
'maxiter': 1,
'miniter': 1,
'accuracy': self.cmd.opts['accuracy'],
'helicity': run_card['nhel_survey'] if 'nhel_survey' in run_card \
else run_card['nhel'],
'gridmode': -2,
'channel' : G
}
Gdir = pjoin(Pdir, 'G%s' % G)
self.write_parameter_file(pjoin(Gdir, 'input_app.txt'), options)
# 2. check that ftn25 exists.
assert os.path.exists(pjoin(Gdir, "ftn25"))
# 3. Submit the new jobs
#call back function
packet = cluster.Packet((Pdir, G, step+1),
self.combine_iteration,
(Pdir, G, step+1))
if step ==0:
self.lastoffset[(Pdir, G)] = 0
# resubmit the new jobs
for i in xrange(int(nb_job)):
name = "G%s_%s" % (G,i+1)
self.lastoffset[(Pdir, G)] += 1
offset = self.lastoffset[(Pdir, G)]
self.cmd.launch_job(pjoin(self.me_dir, 'SubProcesses', 'refine_splitted.sh'),
argument=[name, 'G%s'%G, offset],
cwd= Pdir,
packet_member=packet)
def submit_to_cluster_splitted(self, job_list):
""" submit the version of the survey with splitted grid creation
"""
#if self.splitted_grid <= 1:
# return self.submit_to_cluster_no_splitting(job_list)
for Pdir, jobs in job_list.items():
if self.splitted_for_dir(Pdir, jobs[0]) <= 1:
return self.submit_to_cluster_no_splitting({Pdir:jobs})
self.write_parameter(parralelization=True, Pdirs=[Pdir])
# launch the job with the appropriate grouping
for job in jobs:
packet = cluster.Packet((Pdir, job, 1), self.combine_iteration, (Pdir, job, 1))
for i in range(self.splitted_for_dir(Pdir, job)):
self.cmd.launch_job(pjoin(self.me_dir, 'SubProcesses', 'survey.sh'),
argument=[i+1, job],
cwd=pjoin(self.me_dir,'SubProcesses' , Pdir),
packet_member=packet)
def combine_iteration(self, Pdir, G, step):
grid_calculator, cross, error = self.combine_grid(Pdir, G, step)
# Compute the number of events used for this run.
nb_events = grid_calculator.target_evt
Gdirs = [] #build the the list of directory
for i in range(self.splitted_for_dir(Pdir, G)):
path = pjoin(Pdir, "G%s_%s" % (G, i+1))
Gdirs.append(path)
# 4. make the submission of the next iteration
# Three cases - less than 3 iteration -> continue
# - more than 3 and less than 5 -> check error
# - more than 5 -> prepare info for refine
need_submit = False
if step < self.min_iterations and cross != 0:
if step == 1:
need_submit = True
else:
across = self.abscross[(Pdir,G)]/(self.sigma[(Pdir,G)]+1e-99)
tot_across = self.get_current_axsec()
if across / tot_across < 1e-6:
need_submit = False
elif error < self.cmd.opts['accuracy'] / 100:
need_submit = False
else:
need_submit = True
elif step >= self.cmd.opts['iterations']:
need_submit = False
elif self.cmd.opts['accuracy'] < 0:
#check for luminosity
raise Exception, "Not Implemented"
elif self.abscross[(Pdir,G)] == 0:
need_submit = False
else:
across = self.abscross[(Pdir,G)]/(self.sigma[(Pdir,G)]+1e-99)
tot_across = self.get_current_axsec()
if across == 0:
need_submit = False
elif across / tot_across < 1e-5:
need_submit = False
elif error > self.cmd.opts['accuracy']:
need_submit = True
else:
need_submit = False
if cross:
grid_calculator.write_grid_for_submission(Pdir,G,
self.splitted_for_dir(Pdir, G),
nb_events,mode=self.mode,
conservative_factor=5.0)
xsec_format = '.%ig'%(max(3,int(math.log10(1.0/float(error)))+2)
if float(cross)!=0.0 and float(error)!=0.0 else 8)
if need_submit:
message = "%%s/G%%s is at %%%s +- %%.3g pb. Now submitting iteration #%s."%(xsec_format, step+1)
logger.info(message%\
(os.path.basename(Pdir), G, float(cross),
float(error)*float(cross)))
self.resubmit_survey(Pdir,G, Gdirs, step)
elif cross:
logger.info("Survey finished for %s/G%s at %s"%(
os.path.basename(Pdir),G,('%%%s +- %%.3g pb'%xsec_format))%
(float(cross), float(error)*float(cross)))
# prepare information for refine
newGpath = pjoin(self.me_dir,'SubProcesses' , Pdir, 'G%s' % G)
if not os.path.exists(newGpath):
os.mkdir(newGpath)
# copy the new grid:
files.cp(pjoin(Gdirs[0], 'ftn25'),
pjoin(self.me_dir,'SubProcesses' , Pdir, 'G%s' % G, 'ftn26'))
# copy the events
fsock = open(pjoin(newGpath, 'events.lhe'), 'w')
for Gdir in Gdirs:
fsock.write(open(pjoin(Gdir, 'events.lhe')).read())
# copy one log
files.cp(pjoin(Gdirs[0], 'log.txt'),
pjoin(self.me_dir,'SubProcesses' , Pdir, 'G%s' % G))
# create the appropriate results.dat
self.write_results(grid_calculator, cross, error, Pdir, G, step)
else:
logger.info("Survey finished for %s/G%s [0 cross]", os.path.basename(Pdir),G)
Gdir = pjoin(self.me_dir,'SubProcesses' , Pdir, 'G%s' % G)
if not os.path.exists(Gdir):
os.mkdir(Gdir)
# copy one log
files.cp(pjoin(Gdirs[0], 'log.txt'), Gdir)
# create the appropriate results.dat
self.write_results(grid_calculator, cross, error, Pdir, G, step)
return 0
def combine_grid(self, Pdir, G, step, exclude_sub_jobs=[]):
""" exclude_sub_jobs is to remove some of the subjobs if a numerical
issue is detected in one of them. Warning is issue when this occurs.
"""
# 1. create an object to combine the grid information and fill it
grid_calculator = combine_grid.grid_information(self.run_card['nhel'])
for i in range(self.splitted_for_dir(Pdir, G)):
if i in exclude_sub_jobs:
continue
path = pjoin(Pdir, "G%s_%s" % (G, i+1))
fsock = misc.mult_try_open(pjoin(path, 'results.dat'))
one_result = grid_calculator.add_results_information(fsock)
fsock.close()
if one_result.axsec == 0:
grid_calculator.onefail = True
continue # grid_information might not exists
fsock = misc.mult_try_open(pjoin(path, 'grid_information'))
grid_calculator.add_one_grid_information(fsock)
fsock.close()
os.remove(pjoin(path, 'results.dat'))
#os.remove(pjoin(path, 'grid_information'))
#2. combine the information about the total crossection / error
# start by keep the interation in memory
cross, across, sigma = grid_calculator.get_cross_section()
#3. Try to avoid one single PS point which ruins the integration
# Should be related to loop evaluation instability.
maxwgt = grid_calculator.get_max_wgt(0.01)
if maxwgt:
nunwgt = grid_calculator.get_nunwgt(maxwgt)
# Make sure not to apply the security below during the first step of the
# survey. Also, disregard channels with a contribution relative to the
# total cross-section smaller than 1e-8 since in this case it is unlikely
# that this channel will need more than 1 event anyway.
apply_instability_security = False
rel_contrib = 0.0
if (self.__class__ != gensym or step > 1):
Pdir_across = 0.0
Gdir_across = 0.0
for (mPdir,mG) in self.abscross.keys():
if mPdir == Pdir:
Pdir_across += (self.abscross[(mPdir,mG)]/
(self.sigma[(mPdir,mG)]+1e-99))
if mG == G:
Gdir_across += (self.abscross[(mPdir,mG)]/
(self.sigma[(mPdir,mG)]+1e-99))
rel_contrib = abs(Gdir_across/(Pdir_across+1e-99))
if rel_contrib > (1.0e-8) and \
nunwgt < 2 and len(grid_calculator.results) > 1:
apply_instability_security = True
if apply_instability_security:
# check the ratio between the different submit
th_maxwgt = [(r.th_maxwgt,i) for i,r in enumerate(grid_calculator.results)]
th_maxwgt.sort()
ratio = th_maxwgt[-1][0]/th_maxwgt[-2][0]
if ratio > 1e4:
logger.warning(
""""One Event with large weight have been found (ratio = %.3g) in channel G%s (with rel.contrib=%.3g).
This is likely due to numerical instabilities. The associated job is discarded to recover.
For offline investigation, the problematic discarded events are stored in:
%s"""%(ratio,G,rel_contrib,pjoin(Pdir,'DiscardedUnstableEvents')))
exclude_sub_jobs = list(exclude_sub_jobs)
exclude_sub_jobs.append(th_maxwgt[-1][1])
grid_calculator.results.run_statistics['skipped_subchannel'] += 1
# Add some monitoring of the problematic events
gPath = pjoin(Pdir, "G%s_%s" % (G, th_maxwgt[-1][1]+1))
if os.path.isfile(pjoin(gPath,'events.lhe')):
lhe_file = lhe_parser.EventFile(pjoin(gPath,'events.lhe'))
discardedPath = pjoin(Pdir,'DiscardedUnstableEvents')
if not os.path.exists(discardedPath):
os.mkdir(discardedPath)
if os.path.isdir(discardedPath):
# Keep only the event with a maximum weight, as it surely
# is the problematic one.
evtRecord = open(pjoin(discardedPath,'discarded_G%s.dat'%G),'a')
lhe_file.seek(0) #rewind the file
try:
evtRecord.write('\n'+str(max(lhe_file,key=lambda evt:abs(evt.wgt))))
except Exception:
#something wrong write the full file.
lhe_file.close()
evtRecord.write(pjoin(gPath,'events.lhe').read())
evtRecord.close()
return self.combine_grid(Pdir, G, step, exclude_sub_jobs)
if across !=0:
if sigma != 0:
self.cross[(Pdir,G)] += cross**3/sigma**2
self.abscross[(Pdir,G)] += across * cross**2/sigma**2
self.sigma[(Pdir,G)] += cross**2/ sigma**2
self.chi2[(Pdir,G)] += cross**4/sigma**2
# and use those iteration to get the current estimator
cross = self.cross[(Pdir,G)]/self.sigma[(Pdir,G)]
if step > 1:
error = math.sqrt(abs((self.chi2[(Pdir,G)]/cross**2 - \
self.sigma[(Pdir,G)])/(step-1))/self.sigma[(Pdir,G)])
else:
error = sigma/cross
else:
self.cross[(Pdir,G)] = cross
self.abscross[(Pdir,G)] = across
self.sigma[(Pdir,G)] = 0
self.chi2[(Pdir,G)] = 0
cross = self.cross[(Pdir,G)]
error = 0
else:
error = 0
grid_calculator.results.compute_values(update_statistics=True)
if (str(os.path.basename(Pdir)), G) in self.run_statistics:
self.run_statistics[(str(os.path.basename(Pdir)), G)]\
.aggregate_statistics(grid_calculator.results.run_statistics)
else:
self.run_statistics[(str(os.path.basename(Pdir)), G)] = \
grid_calculator.results.run_statistics
self.warnings_from_statistics(G, grid_calculator.results.run_statistics)
stats_msg = grid_calculator.results.run_statistics.nice_output(
'/'.join([os.path.basename(Pdir),'G%s'%G]))
if stats_msg:
logger.log(5, stats_msg)
# Clean up grid_information to avoid border effects in case of a crash
for i in range(self.splitted_for_dir(Pdir, G)):
path = pjoin(Pdir, "G%s_%s" % (G, i+1))
try:
os.remove(pjoin(path, 'grid_information'))
except OSError, oneerror:
if oneerror.errno != 2:
raise
return grid_calculator, cross, error
def warnings_from_statistics(self,G,stats):
"""Possible warn user for worrying MadLoop stats for this channel."""
if stats['n_madloop_calls']==0:
return
EPS_fraction = float(stats['exceptional_points'])/stats['n_madloop_calls']
msg = "Channel %s has encountered a fraction of %.3g\n"+ \
"of numerically unstable loop matrix element computations\n"+\
"(which could not be rescued using quadruple precision).\n"+\
"The results might not be trusted."
if 0.01 > EPS_fraction > 0.001:
logger.warning(msg%(G,EPS_fraction))
elif EPS_fraction > 0.01:
logger.critical((msg%(G,EPS_fraction)).replace('might', 'can'))
raise Exception, (msg%(G,EPS_fraction)).replace('might', 'can')
def get_current_axsec(self):
across = 0
for (Pdir,G) in self.abscross:
across += self.abscross[(Pdir,G)]/(self.sigma[(Pdir,G)]+1e-99)
return across
def write_results(self, grid_calculator, cross, error, Pdir, G, step):
#compute the value
if cross == 0:
abscross,nw, luminosity = 0, 0, 0
wgt, maxit,nunwgt, wgt, nevents = 0,0,0,0,0
maxwgt = 0
error = 0
else:
grid_calculator.results.compute_values()
abscross = self.abscross[(Pdir,G)]/self.sigma[(Pdir,G)]
nw = grid_calculator.results.nw
wgt = grid_calculator.results.wgt
maxit = step
wgt = 0
nevents = grid_calculator.results.nevents
maxwgt = grid_calculator.get_max_wgt()
nunwgt = grid_calculator.get_nunwgt()
luminosity = nunwgt/cross
#format the results.dat
def fstr(nb):
data = '%E' % nb
nb, power = data.split('E')
nb = float(nb) /10
power = int(power) + 1
return '%.5fE%+03i' %(nb,power)
line = '%s %s %s %i %i %i %i %s %s %s %s 0.0 0\n' % \
(fstr(cross), fstr(error*cross), fstr(error*cross),
nevents, nw, maxit,nunwgt,
fstr(luminosity), fstr(wgt), fstr(abscross), fstr(maxwgt))
fsock = open(pjoin(self.me_dir,'SubProcesses' , Pdir, 'G%s' % G,
'results.dat'),'w')
fsock.writelines(line)
fsock.close()
def resubmit_survey(self, Pdir, G, Gdirs, step):
"""submit the next iteration of the survey"""
# 1. write the new input_app.txt to double the number of points
run_card = self.cmd.run_card
options = {'event' : 2**(step) * self.cmd.opts['points'] / self.splitted_grid,
'maxiter': 1,
'miniter': 1,
'accuracy': self.cmd.opts['accuracy'],
'helicity': run_card['nhel_survey'] if 'nhel_survey' in run_card \
else run_card['nhel'],
'gridmode': -2,
'channel' : ''
}
if int(options['helicity']) == 1:
options['event'] = options['event'] * 2**(self.cmd.proc_characteristics['nexternal']//3)
for Gdir in Gdirs:
self.write_parameter_file(pjoin(Gdir, 'input_app.txt'), options)
#2. resubmit the new jobs
packet = cluster.Packet((Pdir, G, step+1), self.combine_iteration, \
(Pdir, G, step+1))
nb_step = len(Gdirs) * (step+1)
for i,subdir in enumerate(Gdirs):
subdir = subdir.rsplit('_',1)[1]
subdir = int(subdir)
offset = nb_step+i+1
offset=str(offset)
tag = "%s.%s" % (subdir, offset)
self.cmd.launch_job(pjoin(self.me_dir, 'SubProcesses', 'survey.sh'),
argument=[tag, G],
cwd=pjoin(self.me_dir,'SubProcesses' , Pdir),
packet_member=packet)
def write_parameter_file(self, path, options):
""" """
template =""" %(event)s %(maxiter)s %(miniter)s !Number of events and max and min iterations
%(accuracy)s !Accuracy
%(gridmode)s !Grid Adjustment 0=none, 2=adjust
1 !Suppress Amplitude 1=yes
%(helicity)s !Helicity Sum/event 0=exact
%(channel)s """
options['event'] = int(options['event'])
open(path, 'w').write(template % options)
def write_parameter(self, parralelization, Pdirs=None):
"""Write the parameter of the survey run"""
run_card = self.cmd.run_card
options = {'event' : self.cmd.opts['points'],
'maxiter': self.cmd.opts['iterations'],
'miniter': self.min_iterations,
'accuracy': self.cmd.opts['accuracy'],
'helicity': run_card['nhel_survey'] if 'nhel_survey' in run_card \
else run_card['nhel'],
'gridmode': 2,
'channel': ''
}
if int(options['helicity'])== 1:
options['event'] = options['event'] * 2**(self.cmd.proc_characteristics['nexternal']//3)
if parralelization:
options['gridmode'] = -2
options['maxiter'] = 1 #this is automatic in dsample anyway
options['miniter'] = 1 #this is automatic in dsample anyway
options['event'] /= self.splitted_grid
if not Pdirs:
Pdirs = self.subproc
for Pdir in Pdirs:
path =pjoin(Pdir, 'input_app.txt')
self.write_parameter_file(path, options)
class gen_ximprove(object):
# some hardcoded value which impact the generation
gen_events_security = 1.2 # multiply the number of requested event by this number for security
combining_job = 0 # allow to run multiple channel in sequence
max_request_event = 1000 # split jobs if a channel if it needs more than that
max_event_in_iter = 5000
min_event_in_iter = 1000
max_splitting = 130 # maximum duplication of a given channel
min_iter = 3
max_iter = 9
keep_grid_for_refine = False # only apply if needed to split the job
#convenient shortcut for the formatting of variable
@ staticmethod
def format_variable(*args):
return bannermod.ConfigFile.format_variable(*args)
def __new__(cls, cmd, opt):
"""Choose in which type of refine we want to be"""
if cmd.proc_characteristics['loop_induced']:
return super(gen_ximprove, cls).__new__(gen_ximprove_share, cmd, opt)
elif gen_ximprove.format_variable(cmd.run_card['gridpack'], bool):
raise Exception, "Not implemented"
elif cmd.run_card["job_strategy"] == 2:
return super(gen_ximprove, cls).__new__(gen_ximprove_share, cmd, opt)
else:
return super(gen_ximprove, cls).__new__(gen_ximprove_v4, cmd, opt)
def __init__(self, cmd, opt=None):
try:
super(gen_ximprove, self).__init__(cmd, opt)
except TypeError:
pass
self.run_statistics = {}
self.cmd = cmd
self.run_card = cmd.run_card
run_card = self.run_card
self.me_dir = cmd.me_dir
#extract from the run_card the information that we need.
self.gridpack = run_card['gridpack']
self.nhel = run_card['nhel']
if "nhel_refine" in run_card:
self.nhel = run_card["nhel_refine"]
if self.run_card['refine_evt_by_job'] != -1:
self.max_request_event = run_card['refine_evt_by_job']
# Default option for the run
self.gen_events = True
self.min_iter = 3
self.parralel = False
# parameter which was input for the normal gen_ximprove run
self.err_goal = 0.01
self.max_np = 9
self.split_channels = False
# parameter for the gridpack run
self.nreq = 2000
self.iseed = 4321
self.ngran = 1
# placeholder for information
self.results = 0 #updated in launch/update_html
if isinstance(opt, dict):
self.configure(opt)
elif isinstance(opt, bannermod.GridpackCard):
self.configure_gridpack(opt)
def __call__(self):
return self.launch()
def launch(self):
"""running """
#start the run
self.handle_seed()
self.results = sum_html.collect_result(self.cmd, None)
if self.gen_events:
# We run to provide a given number of events
self.get_job_for_event()
else:
# We run to achieve a given precision
self.get_job_for_precision()
def configure(self, opt):
"""Defines some parameter of the run"""
for key, value in opt.items():
if key in self.__dict__:
targettype = type(getattr(self, key))
setattr(self, key, self.format_variable(value, targettype, key))
else:
raise Exception, '%s not define' % key
# special treatment always do outside the loop to avoid side effect
if 'err_goal' in opt:
if self.err_goal < 1:
logger.info("running for accuracy %s%%" % (self.err_goal*100))
self.gen_events = False
elif self.err_goal >= 1:
logger.info("Generating %s unweigthed events." % self.err_goal)
self.gen_events = True
self.err_goal = self.err_goal * self.gen_events_security # security
def handle_seed(self):
"""not needed but for gridpack --which is not handle here for the moment"""
return
def find_job_for_event(self):
"""return the list of channel that need to be improved"""
assert self.err_goal >=1
self.err_goal = int(self.err_goal)
goal_lum = self.err_goal/(self.results.axsec+1e-99) #pb^-1
logger.info('Effective Luminosity %s pb^-1', goal_lum)
all_channels = sum([list(P) for P in self.results],[])
all_channels.sort(cmp= lambda x,y: 1 if y.get('luminosity') - \
x.get('luminosity') > 0 else -1)
to_refine = []
for C in all_channels:
if C.get('axsec') == 0:
continue
if goal_lum/(C.get('luminosity')+1e-99) >= 1 + (self.gen_events_security-1)/2:
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'))
to_refine.append(C)
elif C.get('xerr') > max(C.get('axsec'),
(1/(100*math.sqrt(self.err_goal)))*all_channels[-1].get('axsec')):
to_refine.append(C)
logger.info('need to improve %s channels' % len(to_refine))
return goal_lum, to_refine
def update_html(self):
"""update the html from this object since it contains all the information"""
run = self.cmd.results.current['run_name']
if not os.path.exists(pjoin(self.cmd.me_dir, 'HTML', run)):
os.mkdir(pjoin(self.cmd.me_dir, 'HTML', run))
unit = self.cmd.results.unit
P_text = ""
if self.results:
Presults = self.results
else:
self.results = sum_html.collect_result(self.cmd, None)
Presults = self.results
for P_comb in Presults:
P_text += P_comb.get_html(run, unit, self.cmd.me_dir)
Presults.write_results_dat(pjoin(self.cmd.me_dir,'SubProcesses', 'results.dat'))
fsock = open(pjoin(self.cmd.me_dir, 'HTML', run, 'results.html'),'w')
fsock.write(sum_html.results_header)
fsock.write('%s