diff --git a/Generators/EvgenJobTransforms/share/LHEonly.py b/Generators/EvgenJobTransforms/share/LHEonly.py
new file mode 100644
index 0000000000000000000000000000000000000000..2413fc5a7ca91c42371c2271555bca38932f9b11
--- /dev/null
+++ b/Generators/EvgenJobTransforms/share/LHEonly.py
@@ -0,0 +1,37 @@
+# Common fragment for generation of 1 Pythia8 event, when producing LHE output only
+# It is needed to make the transform run
+# No output is kept from this generation
+## Base config for Pythia8
+
+from Pythia8_i.Pythia8_iConf import Pythia8_i
+genSeq += Pythia8_i("Pythia8")
+
+genSeq.Pythia8.Commands += [
+    "Main:timesAllowErrors = 500",
+    "6:m0 = 172.5",
+    "23:m0 = 91.1876",
+    "23:mWidth = 2.4952",
+    "24:m0 = 80.399",
+    "24:mWidth = 2.085",
+    "StandardModel:sin2thetaW = 0.23113",
+    "StandardModel:sin2thetaWbar = 0.23146",
+    "ParticleDecays:limitTau0 = on",
+    "ParticleDecays:tau0Max = 10.0"]
+
+genSeq.Pythia8.Commands += [
+    "Tune:pp = 5",
+    "MultipartonInteractions:bProfile = 4",
+    "MultipartonInteractions:a1 = 0.03",
+    "MultipartonInteractions:pT0Ref = 1.90",
+    "MultipartonInteractions:ecmPow = 0.30",
+    "PDF:pSet=LHAPDF6:MSTW2008lo68cl",
+    "ColourReconnection:range=2.28",
+    "SoftQCD:inelastic = on",
+    "SpaceShower:rapidityOrder=0"]
+
+evgenConfig.minevents = 1
+del testSeq.TestHepMC
+
+
+
+
diff --git a/Generators/EvgenJobTransforms/share/skeleton.GENtoTXT.py b/Generators/EvgenJobTransforms/share/skeleton.GENtoTXT.py
new file mode 100644
index 0000000000000000000000000000000000000000..a9fe6cf6ec16695e8140e486a0fae0509d4b1666
--- /dev/null
+++ b/Generators/EvgenJobTransforms/share/skeleton.GENtoTXT.py
@@ -0,0 +1,706 @@
+"""Functionality core of the Generate_tf transform"""
+
+##==============================================================
+## Basic configuration
+##==============================================================
+
+## Create sequences for generators, clean-up algs, filters and analyses
+## and import standard framework objects with standard local scope names
+import ast
+import os, re, string, subprocess
+import AthenaCommon.AlgSequence as acas
+import AthenaCommon.AppMgr as acam
+from AthenaCommon.AthenaCommonFlags import jobproperties
+from AthenaCommon.AthenaCommonFlags import athenaCommonFlags
+from AthenaCommon.AthenaCommonFlags import jobproperties
+theApp = acam.theApp
+acam.athMasterSeq += acas.AlgSequence("EvgenGenSeq")
+genSeq = acam.athMasterSeq.EvgenGenSeq
+acam.athMasterSeq += acas.AlgSequence("EvgenFixSeq")
+fixSeq = acam.athMasterSeq.EvgenFixSeq
+acam.athMasterSeq += acas.AlgSequence("EvgenPreFilterSeq")
+prefiltSeq = acam.athMasterSeq.EvgenPreFilterSeq
+acam.athFilterSeq += acas.AlgSequence("EvgenTestSeq")
+testSeq = acam.athFilterSeq.EvgenTestSeq
+## NOTE: LogicalExpressionFilter is an algorithm, not a sequence
+from EvgenProdTools.LogicalExpressionFilter import LogicalExpressionFilter
+acam.athFilterSeq += LogicalExpressionFilter("EvgenFilterSeq")
+filtSeq = acam.athFilterSeq.EvgenFilterSeq
+topSeq = acas.AlgSequence()
+anaSeq = topSeq
+topSeq += acas.AlgSequence("EvgenPostSeq")
+postSeq = topSeq.EvgenPostSeq
+##==============================================================
+## Configure standard Athena services
+##==============================================================
+
+## Special setup for event generation
+include("AthenaCommon/Atlas_Gen.UnixStandardJob.py")
+include("PartPropSvc/PartPropSvc.py")
+
+## Run performance monitoring (memory logging)
+from PerfMonComps.PerfMonFlags import jobproperties as perfmonjp
+perfmonjp.PerfMonFlags.doMonitoring = True
+perfmonjp.PerfMonFlags.doSemiDetailedMonitoring = True
+
+## Jobs should stop if an include fails.
+jobproperties.AthenaCommonFlags.AllowIgnoreConfigError = False
+
+## Set up a standard logger
+from AthenaCommon.Logging import logging
+evgenLog = logging.getLogger('Generate_lhe')
+
+
+##==============================================================
+## Run arg handling
+##==============================================================
+
+## Announce arg checking
+evgenLog.debug("****************** CHECKING EVENT GENERATION ARGS *****************")
+evgenLog.debug(str(runArgs))
+print ("****************** CHECKING EVENT GENERATION ARGS *****************")
+
+## Announce start of job configuration
+evgenLog.debug("****************** CONFIGURING EVENT GENERATION *****************")
+print ("****************** CONFIGURING EVENT GENERATION *****************")
+## Functions for operating on generator names
+## NOTE: evgenConfig, topSeq, svcMgr, theApp, etc. should NOT be explicitly re-imported in JOs
+from EvgenJobTransforms.EvgenConfig import evgenConfig
+from EvgenJobTransforms.EvgenConfig import gens_known, gens_lhef, gen_sortkey, gens_testhepmc, gens_notune, gen_require_steering
+
+
+## Ensure that an output name has been given
+# TODO: Allow generation without writing an output file (if outputEVNTFile is None)?
+#if not hasattr(runArgs, "outputEVNTFile") and not hasattr(runArgs, "outputEVNT_PreFile"):
+#    raise RuntimeError("No output evgen EVNT or EVNT_Pre file provided.")
+
+##==============================================================
+## Configure standard Athena and evgen services
+##==============================================================
+
+## Announce start of job configuration
+evgenLog.debug("****************** CONFIGURING MATRIX ELEMENT GENERATION *****************")
+print ("****************** CONFIGURING MATRIX ELEMENT GENERATION *****************")
+evgenLog.debug("****************** CONFIGURING EVENT GENERATION *****************")
+
+## Functions for operating on generator names
+## NOTE: evgenConfig, topSeq, svcMgr, theApp, etc. should NOT be explicitly re-imported in JOs
+from EvgenJobTransforms.EvgenConfig import evgenConfig
+from EvgenJobTransforms.EvgenConfig import gens_known, gens_lhef, gen_sortkey, gens_testhepmc, gens_notune, gen_require_steering
+
+## Configure the event counting (AFTER all filters)
+# TODO: Rewrite in Python?
+from EvgenProdTools.EvgenProdToolsConf import CountHepMC
+svcMgr.EventSelector.FirstEvent = runArgs.firstEvent
+theApp.EvtMax = -1
+
+#evgenConfig.minevents = 1
+if not hasattr(postSeq, "CountHepMC"):
+    postSeq += CountHepMC()
+
+postSeq.CountHepMC.FirstEvent = runArgs.firstEvent
+postSeq.CountHepMC.CorrectHepMC = False
+postSeq.CountHepMC.CorrectEventID = False
+
+
+##==============================================================
+## Pre- and main config parsing
+##==============================================================
+
+## Announce JO loading
+evgenLog.debug("****************** LOADING PRE-INCLUDES AND JOB CONFIG *****************")
+print ("****************** LOADING PRE-INCLUDES AND JOB CONFIG *****************")
+
+## Pre-include
+if hasattr(runArgs, "preInclude"):
+    for fragment in runArgs.preInclude:
+        include(fragment)
+
+## Pre-exec
+if hasattr(runArgs, "preExec"):
+    evgenLog.info("Transform pre-exec")
+    for cmd in runArgs.preExec:
+        evgenLog.info(cmd)
+        exec(cmd)
+
+# TODO: Explain!!!
+def OutputTXTFile():
+    outputTXTFile = None
+    if hasattr(runArgs,"outputTXTFile"): outputTXTFile=runArgs.outputTXTFile
+    return outputTXTFile
+
+## Main job option include
+## Only permit one jobConfig argument for evgen: does more than one _ever_ make sense?
+if len(runArgs.jobConfig) != 1:
+    evgenLog.error("You must supply one and only one jobConfig file argument")
+    sys.exit(1)
+jo = runArgs.jobConfig[0]
+print jo
+jofile = os.path.basename(jo)
+joparts = jofile.split(".")
+## Perform some consistency checks if this appears to be an "official" production JO
+officialJO = False
+if joparts[0].startswith("MC") and all(c in string.digits for c in joparts[0][2:]):
+    officialJO = True
+    ## Check that the JO does not appear to be an old one, since we can't use those
+    if int(joparts[0][2:]) < 14:
+        evgenLog.error("MC14 (or later) job option scripts are needed to work with Generate_tf!")
+        evgenLog.error(jo + " will not be processed: please rename or otherwise update to a >= MC14 JO.")
+        sys.exit(1)
+    ## Check that there are exactly 4 name parts separated by '.': MCxx, DSID, physicsShort, .py
+    if len(joparts) != 4:
+        evgenLog.error(jofile + " name format is wrong: must be of the form MC<xx>.<yyyyyy>.<physicsShort>.py: please rename.")
+        sys.exit(1)
+    ## Check the DSID part of the name
+    jo_dsidpart = joparts[1]
+    try:
+        jo_dsidpart = int(jo_dsidpart)
+        if runArgs.runNumber != jo_dsidpart:
+            raise Exception()
+    except:
+        evgenLog.error("Expected dataset ID part of JO name to be '%s', but found '%s'" % 
+(str(runArgs.runNumber), jo_dsidpart))
+        sys.exit(1)
+    ## Check the length limit on the physicsShort portion of the filename
+    jo_physshortpart = joparts[2]
+    if len(jo_physshortpart) > 60:
+        evgenLog.error(jofile + " contains a physicsShort field of more than 60 characters: please rename.")
+        sys.exit(1)
+    ## There must be at least 2 physicsShort sub-parts separated by '_': gens, (tune)+PDF, and process
+    jo_physshortparts = jo_physshortpart.split("_")
+    if len(jo_physshortparts) < 2:
+        evgenLog.error(jofile + " has too few physicsShort fields separated by '_': should contain <generators>(_<tune+PDF_if_available>)_<process>. Please rename.")
+        sys.exit(1)
+    ## NOTE: a further check on physicsShort consistency is done below, after fragment loading
+
+## Include the JO fragment
+include(jo)
+include('~/workarea/AthGeneration/athena/Generators/EvgenJobTransforms/share/LHEonly.py')
+
+##==============================================================
+## Config validation and propagation to services, generators, etc.
+##==============================================================
+
+## Announce start of JO checking
+evgenLog.debug("****************** CHECKING EVGEN CONFIGURATION *****************")
+print ("****************** CHECKING EVGEN CONFIGURATION *****************")
+
+## Print out options
+for opt in str(evgenConfig).split(os.linesep):
+    evgenLog.info(opt)
+
+## Sort and check generator name / JO name consistency
+##
+## Check that the generators list is not empty:
+if not evgenConfig.generators:
+    evgenLog.error("No entries in evgenConfig.generators: invalid configuration, please check your JO")
+    sys.exit(1)
+## Check for duplicates:
+if len(evgenConfig.generators) > len(set(evgenConfig.generators)):
+    evgenLog.error("Duplicate entries in evgenConfig.generators: invalid configuration, please check your JO")
+    sys.exit(1)
+## Sort the list of generator names into standard form
+gennames = sorted(evgenConfig.generators, key=gen_sortkey)
+## Check that the actual generators, tune, and main PDF are consistent with the JO name
+if joparts[0].startswith("MC"): #< if this is an "official" JO
+    genpart = jo_physshortparts[0]
+#    genpart = genpart.replace("Py8", "Pythia8").replace("MG","MadGraph").replace("Ph","Powheg").replace("Hpp",Herwigpp").replace("H7",Herwig7").replace("Sh","Sherpa").replace("Ag","Alpgen").replace("Py","Pythia").replace("EG","EvtGen").replace("PG","ParticleGun")
+    expectedgenpart = ''.join(gennames)
+    ## We want to record that HERWIG was used in metadata, but in the JO naming we just use a "Herwig" label
+    expectedgenpart = expectedgenpart.replace("HerwigJimmy", "Herwig")
+    def _norm(s):
+        # TODO: add EvtGen to this normalization for MC14?
+        return s.replace("Photospp", "").replace("Photos", "").replace("TauolaPP", "").replace("Tauolapp", "").replace("Tauola", "")
+    def _norm2(s):
+        return s.replace("Py", "Pythia").replace("MG","MadGraph").replace("Ph","Powheg").replace("Hpp","Herwigpp").replace("H7","Herwig7").replace("Sh","Sherpa").replace("Ag","Alpgen").replace("EG","EvtGen").replace("PG","ParticleGun").replace("Gva","Geneva")
+        
+    def _short2(s):
+         return s.replace("Pythia","Py").replace("MadGraph","MG").replace("Powheg","Ph").replace("Herwigpp","Hpp").replace("Herwig7","H7").replace("Sherpa","Sh").replace("Alpgen","Ag").replace("EvtGen","EG").replace("PG","ParticleGun").replace("Geneva","Gva")
+     
+#    if genpart != expectedgenpart and _norm(genpart) != _norm(expectedgenpart) and _norm2(genpart) != expectedgenpart and _norm2(genpart) != _norm(expectedgenpart):
+#        evgenLog.error("Expected first part of JO name to be '%s' or '%s' or '%s', but found '%s'" % (_norm(expectedgenpart), expectedgenpart, _short2(expectedgenpart), genpart))
+#        evgenLog.error("gennames '%s' " %(expectedgenpart))
+#        sys.exit(1)
+
+    if genpart != _norm(expectedgenpart)  and _norm2(genpart) != _norm(expectedgenpart):
+        evgenLog.error("Expected first part of JO name to be '%s' or '%s', but found '%s'" % (_norm(expectedgenpart), _norm(_short2(expectedgenpart)), genpart))
+        evgenLog.error("gennames '%s' " %(expectedgenpart))
+        sys.exit(1)
+
+    del _norm
+    ## Check if the tune/PDF part is needed, and if so whether it's present
+    if not gens_notune(gennames) and len(jo_physshortparts) < 3:
+        evgenLog.error(jofile + " with generators " + expectedgenpart +
+                       " has too few physicsShort fields separated by '_'." +
+                       " It should contain <generators>_<tune+PDF_<process>. Please rename.")
+        sys.exit(1)
+
+## Check the "--steering=afterburn" command line argument has been set if EvtGen is in the JO name
+# Dont't have access to steering flag so check it's effect on output files
+if gen_require_steering(gennames):
+    if hasattr(runArgs, "outputEVNTFile") and not hasattr(runArgs, "outputEVNT_PreFile"):
+        raise RuntimeError("'EvtGen' found in job options name, please set '--steering=afterburn'")
+
+
+## Check that the evgenConfig.minevents setting is acceptable
+## minevents defines the production event sizes and must be sufficiently "round"    
+rounding = 0
+if hasattr(runArgs,'inputGeneratorFile') and ',' in runArgs.inputGeneratorFile:   multiInput = runArgs.inputGeneratorFile.count(',')+1
+else:
+   multiInput = 0
+   
+if evgenConfig.minevents < 0:
+    raise RunTimeError("evgenConfig.minevents must be at least 0")
+else:
+    allowed_minevents_lt1000 = [0, 1, 2, 5, 10, 20, 25, 50, 100, 200, 500, 1000]
+    msg = "evgenConfig.minevents = %d: " % evgenConfig.minevents
+# introduced due to PRODSYS-788, commented out on 06.07.18 obo Dominic
+#    if multiInput !=0 :
+#        dummy_minevents = evgenConfig.minevents*(multiInput)
+#        evgenLog.info('Replacing input minevents '+str(evgenConfig.minevents)+' with calculated '+str(dummy_minevents))
+#        evgenConfig.minevents = dummy_minevents
+
+    if evgenConfig.minevents >= 1000 and evgenConfig.minevents % 1000 != 0:
+# introduced due to PRODSYS-788, commented out on 06.07.18 obo Dominic
+#        rest1000 = evgenConfig.minevents % 1000
+#        if multiInput !=0 :
+#            rounding=1
+#            if rest1000 < 1000-rest1000:
+#                evgenLog.info('Replacing minevents '+str(evgenConfig.minevents)+' with roundeded '+str(evgenConfig.minevents-rest1000))
+#                evgenConfig.minevents = evgenConfig.minevents-rest1000
+#            else:
+#                evgenLog.info('Replacing input minevents '+str(evgenConfig.minevents)+' with calculated '+str(evgenConfig.minevents-rest1000+1000))
+#                evgenConfig.minevents = evgenConfig.minevents-rest1000+1000
+#        else:    
+           msg += "minevents in range >= 1000 must be a multiple of 1000"
+           raise RuntimeError(msg)
+    elif evgenConfig.minevents < 1000 and evgenConfig.minevents not in allowed_minevents_lt1000:
+# introduced due to PRODSYS-788, commented out on 06.07.18 obo Dominic
+#        if multiInput !=0:
+#           rounding=1
+#           round_minevents=min(allowed_minevents_lt1000,key=lambda x:abs(x-evgenConfig.minevents))
+#           evgenLog.info('Replacing minevents lt 1000 '+str(evgenConfig.minevents)+' with rounded '+str(round_minevents))
+#           evgenConfig.minevents=round_minevents
+#        else:
+           msg += "minevents in range <= 1000 must be one of %s" % allowed_minevents_lt1000
+           raise RuntimeError(msg)
+#    else:
+#    postSeq.CountHepMC.RequestedOutput = evgenConfig.minevents if runArgs.maxEvents == -1 or rounding==1 else runArgs.maxEvents
+    postSeq.CountHepMC.RequestedOutput = evgenConfig.minevents if runArgs.maxEvents == -1  else runArgs.maxEvents
+    evgenLog.info('Requested output events '+str(postSeq.CountHepMC.RequestedOutput))
+
+## Check that the keywords list is not empty:
+if not evgenConfig.keywords:
+    evgenLog.warning("No entries in evgenConfig.keywords: invalid configuration, please check your JO !!")    
+
+## Check that the keywords are in the list of allowed words (and exit if processing an official JO)
+if evgenConfig.keywords:
+    ## Get the allowed keywords file from the JO package if possibe
+    # TODO: Make the package name configurable
+    kwfile = "MC15JobOptions/evgenkeywords.txt"
+    kwpath = None
+    for p in os.environ["JOBOPTSEARCHPATH"].split(":"):
+        kwpath = os.path.join(p, kwfile)
+        if os.path.exists(kwpath):
+            break
+        kwpath = None
+    ## Load the allowed keywords from the file
+    allowed_keywords = []
+    if kwpath:
+        kwf = open(kwpath, "r")
+        for l in kwf:
+            allowed_keywords += l.strip().lower().split()
+#        ## add also categories if in a separate file
+#        kwfile1 = "MC15JobOptions/CategoryList.txt"
+#        kwpath1 = None
+#        for p in os.environ["JOBOPTSEARCHPATH"].split(":"):
+#            kwpath1 = os.path.join(p, kwfile1)
+#            if os.path.exists(kwpath1):
+#               break
+#            kwpath1 = None
+#    ## Continue loading the categories to allowed keywords list
+#            if kwpath1:
+#               kwf1 = open(kwpath1, "r")
+#               for l in kwf1:
+#                  allowed_keywords += l.strip().lower().split()        
+        #allowed_keywords.sort()
+        ## Check the JO keywords against the allowed ones
+        evil_keywords = []
+        ll1 = 0
+        ll2 = 0
+        for k in evgenConfig.keywords:
+            if k.lower() not in allowed_keywords:
+                evil_keywords.append(k)
+#            if "l1." in k.lower():
+#                ll1 +=1
+#            if "l2." in k.lower():    
+#                ll2 +=1        
+        if evil_keywords:
+            msg = "evgenConfig.keywords contains non-standard keywords: %s. " % ", ".join(evil_keywords)
+            msg += "Please check the allowed keywords list and fix."
+            evgenLog.error(msg)
+            if officialJO:
+                sys.exit(1)
+#        if ll1 == 0:
+#            msg = "evgenConfig.keywords do not contain L1. keyword"
+#            evgenLog.warning(msg)
+#        if ll2 == 0:
+#            msg = "evgenConfig.keywords do not contain L2. keyword"
+#            evgenLog.warning(msg) 
+    else:
+        evgenLog.warning("Could not find evgenkeywords.txt file %s in $JOBOPTSEARCHPATH" % kwfile)
+
+## Check that the categories list is not empty:
+if not evgenConfig.categories:
+    evgenLog.warning("No entries in evgenConfig.categories: invalid configuration, please check your JO !!")        
+
+## Check that the L1 and L2 keywords pairs are in the list of allowed words pairs (and exit if processing an official JO)
+if evgenConfig.categories:
+    ## Get the allowed categories file from the JO package if possibe
+    # TODO: Make the package name configurable
+    lkwfile = "MC15JobOptions/CategoryList.txt"
+    lkwpath = None
+    for p in os.environ["JOBOPTSEARCHPATH"].split(":"):
+        lkwpath = os.path.join(p, lkwfile)
+        if os.path.exists(lkwpath):
+            break
+        lkwpath = None
+## Load the allowed categories names from the file
+    allowed_cat = []
+    if lkwpath:
+        with open(lkwpath, 'r') as catlist:
+            for line in catlist:
+               allowed_list = ast.literal_eval(line)
+               allowed_cat.append(allowed_list)
+# Print allowed categories and categories read from the JOs
+#            print "allowed categories", allowed_list
+#            print "evgenConfig.categories", evgenConfig.categories
+
+        ## Check the JO categories against the allowed ones
+        bad_cat =[]
+        it = iter(evgenConfig.categories)
+        for x in it:
+           l1 = x
+           l2 = next(it)
+           if "L1:" in l2 and "L2:" in l1:
+               l1, l2 = l2, l1
+           print "first",l1,"second",l2
+           bad_cat.extend([l1, l2])
+           for a1,a2 in allowed_cat:
+#               print "a1 ",a1,"l1 ",l1, "a2 ",a2,"l2 ",l2
+               if l1.strip().lower()==a1.strip().lower() and l2.strip().lower()==a2.strip().lower():
+                 bad_cat=[]
+           if bad_cat:
+               msg = "evgenConfig.categories contains non-standard category: %s. " % ", ".join(bad_cat)
+               msg += "Please check the allowed categories list and fix."
+               evgenLog.error(msg)
+               if officialJO:
+                   sys.exit(1)
+    else:
+        evgenLog.warning("Could not find CategoryList.txt file %s in $JOBOPTSEARCHPATH" % lkwfile)
+
+## Configure and schedule jet finding algorithms
+## NOTE: This generates algorithms for jet containers defined in the user's JO fragment
+if evgenConfig.findJets:
+    include("EvgenJobTransforms/Generate_TruthJets.py")
+
+## Propagate energy argument to the generators
+# TODO: Standardise energy setting in the GenModule interface
+include("EvgenJobTransforms/Generate_ecmenergies.py")
+
+## Process random seed arg and pass to generators
+#include("EvgenJobTransforms/Generate_randomseeds.py")
+
+
+##==============================================================
+## Handling of a post-include/exec args at the end of standard configuration
+##==============================================================
+
+if hasattr(runArgs, "postInclude"):
+    for fragment in runArgs.postInclude:
+        include(fragment)
+
+if hasattr(runArgs, "postExec"):
+    evgenLog.info("Transform post-exec")
+    for cmd in runArgs.postExec:
+        evgenLog.info(cmd)
+        exec(cmd)
+
+
+##==============================================================
+## Input file arg handling
+##==============================================================
+
+## Announce start of input file handling
+evgenLog.debug("****************** HANDLING EVGEN INPUT FILES *****************")
+print("****************** HANDLING EVGEN INPUT FILES *****************")
+## Dat files
+datFile = None
+if "McAtNlo" in evgenConfig.generators and "Herwig" in evgenConfig.generators:
+    datFile = "inparmMcAtNlo.dat"
+elif "Alpgen" in evgenConfig.generators:
+    datFile = "inparmAlpGen.dat"
+elif "Protos" in evgenConfig.generators:
+    datFile = "protos.dat"
+elif "ProtosLHEF" in evgenConfig.generators:
+    datFile = "protoslhef.dat"
+elif "AcerMC" in evgenConfig.generators:
+    datFile = "inparmAcerMC.dat"
+elif "CompHep" in evgenConfig.generators:
+    datFile = "inparmCompHep.dat"
+
+if hasattr(runArgs,"outputTXTFile"): outputTXTFile=runArgs.outputTXTFile
+## Events files
+eventsFile = None
+if "Alpgen" in evgenConfig.generators:
+    eventsFile = "alpgen.unw_events"
+elif "Protos" in evgenConfig.generators: 
+    eventsFile = "protos.events"
+elif "ProtosLHEF" in evgenConfig.generators:
+    eventsFile = "protoslhef.events"
+elif "BeamHaloGenerator" in evgenConfig.generators:
+    eventsFile = "beamhalogen.events"
+elif "HepMCAscii" in evgenConfig.generators:
+    eventsFile = "events.hepmc"
+elif gens_lhef(evgenConfig.generators):
+    eventsFile = outputTXTFile
+#    eventsFile = "events.lhe"
+
+
+## Helper functions for input file handling
+def find_unique_file(pattern):
+    "Return a matching file, provided it is unique"
+    import glob
+    files = glob.glob(pattern)
+    ## Check that there is exactly 1 match
+    if not files:
+        raise RuntimeError("No '%s' file found" % pattern)
+    elif len(files) > 1:
+        raise RuntimeError("More than one '%s' file found" % pattern)
+    return files[0]
+
+# This function merges a list of input LHE file to make one outputFile.  The header is taken from the first
+# file, but the number of events is updated to equal the total number of events in all the input files
+def merge_lhe_files(listOfFiles,outputFile):
+    if(os.path.exists(outputFile)):
+      print "outputFile ",outputFile," already exists.  Will rename to ",outputFile,".OLD"
+      os.rename(outputFile,outputFile+".OLD")
+    output = open(outputFile,'w')
+    holdHeader = ""
+    nevents=0
+    for file in listOfFiles:
+       cmd = "grep /event "+file+" | wc -l"
+       nevents+=int(subprocess.check_output(cmd,stderr=subprocess.STDOUT,shell=True))
+
+    for file in listOfFiles:
+       inHeader = True
+       header = ""
+       print "*** Starting file ",file
+       for line in open(file,"r"):
+##        Reading first event signals that we are done with all the header information
+##        Using this approach means the script will properly handle any metadata stored
+##        at the beginning of the file.  Note:  aside from the number of events, no metadata
+##        is updated after the first header is read (eg the random number seed recorded will be
+##        that of the first file.
+          if("<event" in line and inHeader):
+             inHeader = False
+             if(len(holdHeader)<1):
+                holdHeader = header
+                output.write(header)
+             output.write(line)
+##        each input file ends with "</LesHouchesEvents>".  We don't want to write this out until all
+##        the files have been read.  The elif below writes out all the events.
+          elif(not inHeader and not ("</LesHouchesEvents>" in line)):
+              output.write(line)
+          if(inHeader):
+##           Format for storing number of events different in MG and Powheg 
+             if("nevents" in line):
+##              MG5 format is "n = nevents"
+                tmp = line.split("=")
+                line = line.replace(tmp[0],str(nevents))
+             elif("numevts" in line):
+##              Powheg format is numevts n
+                tmp = line.split(" ")
+                nnn = str(nevents)
+                line = line.replace(tmp[1],nnn)
+             header+=line
+    output.write("</LesHouchesEvents>\n")
+    output.close()
+
+
+def mk_symlink(srcfile, dstfile):
+    "Make a symlink safely"
+    if dstfile:
+        if os.path.exists(dstfile) and not os.path.samefile(dstfile, srcfile):
+            os.remove(dstfile)
+        if not os.path.exists(dstfile):
+            evgenLog.info("Symlinking %s to %s" % (srcfile, dstfile))
+            print "Symlinking %s to %s" % (srcfile, dstfile)
+            os.symlink(srcfile, dstfile)
+        else:
+            evgenLog.debug("Symlinking: %s is already the same as %s" % (dstfile, srcfile))
+
+## Find and symlink dat and event files, so they are available via the name expected by the generator
+if eventsFile or datFile:
+    if not hasattr(runArgs, "inputGeneratorFile") or runArgs.inputGeneratorFile == "NONE":
+        raise RuntimeError("%s needs input file (argument inputGeneratorFile)" % runArgs.jobConfig)
+    if evgenConfig.inputfilecheck and not re.search(evgenConfig.inputfilecheck, runArgs.inputGeneratorFile):
+        raise RuntimeError("inputGeneratorFile=%s is incompatible with inputfilecheck '%s' in %s" %
+                           (runArgs.inputGeneratorFile, evgenConfig.inputfilecheck, runArgs.jobConfig))
+#    inputroot = os.path.basename(runArgs.inputGeneratorFile).split("._")[0]
+    if datFile:
+      if ".tar" in os.path.basename(runArgs.inputGeneratorFile):
+        inputroot = os.path.basename(runArgs.inputGeneratorFile).split(".tar.")[0]
+      else:  
+        inputroot = os.path.basename(runArgs.inputGeneratorFile).split("._")[0]
+
+      realDatFile = find_unique_file('*%s*.dat' % inputroot)
+      mk_symlink(realDatFile, datFile)
+    if eventsFile:
+#        realEventsFile = find_unique_file('*%s.*.ev*ts' % inputroot)
+#        mk_symlink(realEventsFile, eventsFile)
+        myinputfiles = runArgs.inputGeneratorFile
+        genInputFiles = myinputfiles.split(',')
+        numberOfFiles = len(genInputFiles)
+        # if there is a single file, make a symlink.  If multiple files, merge them into one output eventsFile
+        if(numberOfFiles<2):
+           if ".tar" in os.path.basename(runArgs.inputGeneratorFile):
+             inputroot = os.path.basename(runArgs.inputGeneratorFile).split(".tar.")[0]
+           else:  
+             inputroot = os.path.basename(runArgs.inputGeneratorFile).split("._")[0]
+
+           if "events" in inputroot :
+               inputroot = inputroot.replace(".events","")
+           realEventsFile = find_unique_file('*%s.*ev*ts' % inputroot)
+           mk_symlink(realEventsFile, eventsFile)
+        else:
+           allFiles = []
+           for file in genInputFiles:
+#             Since we can have multiple files from the same task, inputroot must include more of the filename
+#             to make it unique
+              if ".tar" in os.path.basename(runArgs.inputGeneratorFile):
+                inputroot = os.path.basename(runArgs.inputGeneratorFile).split(".tar.")[0]
+              else:  
+                input0 = os.path.basename(file).split("._")[0]
+                input1 = (os.path.basename(file).split("._")[1]).split(".")[0]
+                inputroot = input0+"._"+input1
+              print "inputroot ",inputroot
+              realEventsFile = find_unique_file('*%s.*ev*ts' % inputroot)
+#             The only input format where merging is permitted is LHE
+              with open(realEventsFile, 'r') as f:
+                 first_line = f.readline()
+                 if(not ("LesHouche" in first_line)):
+                    raise RuntimeError("%s is NOT a LesHouche file" % realEventsFile)
+                 allFiles.append(realEventsFile)
+           merge_lhe_files(allFiles,eventsFile)
+
+else:
+    if hasattr(runArgs, "inputGeneratorFile") and runArgs.inputGeneratorFile != "NONE":
+        raise RuntimeError("inputGeneratorFile arg specified for %s, but generators %s do not require an input file" %
+                           (runArgs.jobConfig, str(gennames)))
+    if evgenConfig.inputfilecheck:
+        raise RuntimeError("evgenConfig.inputfilecheck specified in %s, but generators %s do not require an input file" %
+                           (runArgs.jobConfig, str(gennames)))
+
+## Check conf files, as above but for a different command line arg, and with omission allowed
+if hasattr(runArgs, "inputGenConfFile") and runArgs.inputGenConfFile != "NONE":
+    if evgenConfig.inputconfcheck and not re.search(evgenConfig.inputconfcheck, runArgs.inputGenConfFile):
+        raise RuntimeError("inputGenConfFile=%s is incompatible with inputconfcheck (%s) in %s" %
+                           (runArgs.inputGenConfFile, evgenConfig.inputconfcheck, runArgs.jobConfig))
+
+## Do the aux-file copying
+if evgenConfig.auxfiles:
+    from PyJobTransformsCore.trfutil import get_files
+    get_files(evgenConfig.auxfiles, keepDir=False, errorIfNotFound=True)
+
+
+##==============================================================
+## Write out metadata for reporting to AMI
+##==============================================================
+
+def _checkattr(attr, required=False):
+    if not hasattr(evgenConfig, attr) or not getattr(evgenConfig, attr):
+        msg = "evgenConfig attribute '%s' not found." % attr
+        if required:
+            raise RuntimeError("Required " + msg)
+        return False
+    return True
+
+if _checkattr("description", required=True):
+    msg = evgenConfig.description
+    if _checkattr("notes"):
+        msg += " " + evgenConfig.notes
+    print "MetaData: %s = %s" % ("physicsComment", msg)
+if _checkattr("generators", required=True):
+#    print "MetaData: %s = %s" % ("generatorName", "+".join(gennames))
+    gennamesvers=[]
+    for item in gennames:
+       genera = item.upper()
+       generat = genera+"VER"
+       if (generat in os.environ):
+           gennamesvers.append(item+"(v."+os.environ[generat]+")")
+#           gennamesvers.append(item+"."+os.environ[generat])
+       else:
+           gennamesvers.append(item)
+    print "MetaData: %s = %s" % ("generatorName", "+".join(gennamesvers))    
+if _checkattr("process"):
+    print "MetaData: %s = %s" % ("physicsProcess", evgenConfig.process)
+if _checkattr("tune"):
+    print "MetaData: %s = %s" % ("generatorTune", evgenConfig.tune)
+if _checkattr("hardPDF"):
+    print "MetaData: %s = %s" % ("hardPDF", evgenConfig.hardPDF)
+if _checkattr("softPDF"):
+    print "MetaData: %s = %s" % ("softPDF", evgenConfig.softPDF)
+if _checkattr("keywords"):
+    print "MetaData: %s = %s" % ("keywords", ", ".join(evgenConfig.keywords).lower() ),
+if _checkattr("categories"):
+    print ( ", " + ", ".join(evgenConfig.categories))
+else:
+    print (" ")
+
+#if _checkattr("categories"):
+#    print "MetaData: %s = %s" % ("categories", ", ".join(evgenConfig.categories))
+if _checkattr("specialConfig"):
+   print "MetaData: %s = %s" % ("specialConfig", evgenConfig.specialConfig)
+# TODO: Require that a contact / JO author is always set
+if _checkattr("contact"):
+    print "MetaData: %s = %s" % ("contactPhysicist", ", ".join(evgenConfig.contact))
+#if _checkattr( "randomSeed") :
+print "MetaData: %s = %s" % ("randomSeed", str(runArgs.randomSeed))
+ 
+    
+    
+
+# Output list of generator filters used
+filterNames = [alg.getType() for alg in acas.iter_algseq(filtSeq)]
+excludedNames = ['AthSequencer', 'PyAthena::Alg', 'TestHepMC']
+filterNames = list(set(filterNames) - set(excludedNames))
+print "MetaData: %s = %s" % ("genFilterNames", ", ".join(filterNames))
+
+
+##==============================================================
+## Dump evgenConfig so it can be recycled in post-run actions
+##==============================================================
+
+from PyJobTransformsCore.runargs import RunArguments
+runPars = RunArguments()
+runPars.minevents = evgenConfig.minevents
+runPars.maxeventsstrategy = evgenConfig.maxeventsstrategy
+with open("config.pickle", 'w') as f:
+    import cPickle
+    cPickle.dump(runPars, f)
+
+
+##==============================================================
+## Get ready to run...
+##==============================================================
+ 
+evgenLog.debug("****************** STARTING EVENT GENERATION *****************")
+print ("****************** STARTING EVENT GENERATION *****************")
+print ("**************************************************************")
+print ("****************** PLEASE IGNORE THE LOG FROM PYTHIA ************")
+print ("****************** GENERATION OF ONE PYTHIA EVENT ***************")
+print ("******************** IS NEEDED TO MAKE *************")
+print ("****************** THE TRANSFORM WORK ***************************")
+print ("**************************************************************")
+