Skip to content
97 changes: 97 additions & 0 deletions Configuration/DataProcessing/python/Impl/l1Scouting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#!/usr/bin/env python3
"""
_l1Scouting_

Scenario supporting proton collisions with input L1-Scouting data

"""
from Configuration.DataProcessing.Scenario import *
from Configuration.DataProcessing.Utils import stepSKIMPRODUCER, addMonitoring, dictIO, nanoFlavours, gtNameAndConnect

import FWCore.ParameterSet.Config as cms

class l1Scouting(Scenario):
def __init__(self):
Scenario.__init__(self)
self.recoSeq = ''
self.cbSc = 'pp'
self.isRepacked = False
self.promptCustoms = ['Configuration/DataProcessing/RecoTLR.customisePrompt']
self.promptModifiers = cms.ModifierChain()
"""
_l1Scouting_

Implement configuration building for data processing for proton
collision data taking with input L1-Scouting data
"""

def promptReco(self, globalTag, **args):
"""
_promptReco_

Proton collision data taking prompt reco with input L1-Scouting data

"""

options = Options()
options.__dict__.update(defaultOptions.__dict__)
options.scenario = self.cbSc

if 'nThreads' in args:
options.nThreads = args['nThreads']

PhysicsSkimStep = ''
if 'PhysicsSkims' in args:
PhysicsSkimStep = stepSKIMPRODUCER(args['PhysicsSkims'])

miniAODStep = ''
nanoAODStep = ''

if 'outputs' in args:
outputs = []
for a in args['outputs']:
if a['dataTier'] in ['NANOAOD', 'NANOEDMAOD']:
if 'nanoFlavours' not in args:
raise SystemExit(f'l1Scouting: fatal error - requesting {a["dataTier"]} dataTier without specifying a NanoFlavour'
' ("nanoFlavours" must be either ["@L1Scout"] or ["@L1ScoutSelect"])')
args_nanoFlavours = args['nanoFlavours']
if args_nanoFlavours not in [['@L1Scout'], ['@L1ScoutSelect']]:
raise SystemExit(f'l1Scouting: fatal error - invalid "nanoFlavours": {args_nanoFlavours}'
' (must be either ["@L1Scout"] or ["@L1ScoutSelect"])')
nanoAODStep = ',NANO' + nanoFlavours(args_nanoFlavours)
outputs.append(a)
else:
print(f'l1Scouting: warning - dataTier:{a["dataTier"]} is currently not supported and will be removed from outputs')
if {output['dataTier'] for output in outputs} != {a['dataTier'] for a in args['outputs']}:
print(f'l1Scouting: warning - the outputs will be changed from {args["outputs"]} to {outputs}')
args['outputs'] = outputs

if not 'customs' in args:
args['customs'] = []

for c in self.promptCustoms:
args['customs'].append(c)
options.customisation_file = args['customs']

options.isRepacked = args.get('repacked', self.isRepacked)

options.step = ''
options.step += self.recoSeq + PhysicsSkimStep
options.step += miniAODStep + nanoAODStep

dictIO(options, args)
options.conditions = gtNameAndConnect(globalTag, args)

process = cms.Process('L1SCOUT', cms.ModifierChain(self.eras, self.promptModifiers))
cb = ConfigBuilder(options, process = process, with_output = True)

# Input source
process.source = cms.Source("PoolSource",
fileNames = cms.untracked.vstring()
)

cb.prepare()

addMonitoring(process)

return process
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env python3
"""
_l1ScoutingEra_Run3_2026_

Scenario supporting proton collisions with input L1-Scouting data for 2026

"""
from Configuration.DataProcessing.Impl.l1Scouting import l1Scouting

from Configuration.Eras.Era_Run3_2026_cff import Run3_2026

class l1ScoutingEra_Run3_2026(l1Scouting):
def __init__(self):
l1Scouting.__init__(self)
self.recoSeq = ''
self.cbSc = 'pp'
self.eras = Run3_2026
self.promptCustoms += ['Configuration/DataProcessing/RecoTLR.customisePostEra_Run3_2026']
"""
_l1ScoutingEra_Run3_2026_
Implement configuration building for data processing for proton
collision data taking with input L1-Scouting data for Era_Run3_2026
"""
14 changes: 13 additions & 1 deletion Configuration/DataProcessing/python/Merge.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def mergeProcess(*inputFiles, **options):
newDQMIO = options.get("newDQMIO", False)
mergeNANO = options.get("mergeNANO", False)
bypassVersionCheck = options.get("bypassVersionCheck", False)
isL1Scouting = options.get("isL1Scouting", False)
# //
# // build process
#//
Expand Down Expand Up @@ -72,7 +73,18 @@ def mergeProcess(*inputFiles, **options):
outMod = OutputModule("DQMRootOutputModule")
elif mergeNANO:
import Configuration.EventContent.EventContent_cff
outMod = OutputModule("NanoAODOutputModule",Configuration.EventContent.EventContent_cff.NANOAODEventContent.clone())
if isL1Scouting:
# For Run-3 L1-Scouting data, the plugin "OrbitNanoAODOutputModule"
# is used in the "merge" step, instead of "NanoAODOutputModule".
# "OrbitNanoAODOutputModule" converts orbit-based NanoAOD tables (EDM)
# to event/BX-based "flat" NanoAOD branches.
outMod = OutputModule("OrbitNanoAODOutputModule",
Configuration.EventContent.EventContent_cff.L1SCOUTNANOAODEventContent.clone(),
# skip BXs in which all the L1-Scouting tables are empty
skipEmptyBXs = CfgTypes.bool(True)
)
else:
outMod = OutputModule("NanoAODOutputModule", Configuration.EventContent.EventContent_cff.NANOAODEventContent.clone())
else:
outMod = OutputModule("PoolOutputModule")
outMod.mergeJob = CfgTypes.untracked.bool(True)
Expand Down
3 changes: 2 additions & 1 deletion Configuration/DataProcessing/test/BuildFile.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@
<test name="TestConfigDP_10" command="run_CfgTest_10.sh"/>
<test name="TestConfigDP_11" command="run_CfgTest_11.sh"/>
<test name="TestConfigDP_12" command="run_CfgTest_12.sh"/>
<test name="TestConfigDP_13" command="run_CfgTest_13.sh"/>
<test name="TestConfigDP_13" command="run_CfgTest_13.sh"/>
<test name="TestConfigDP_14" command="run_CfgTest_14.sh"/>
12 changes: 8 additions & 4 deletions Configuration/DataProcessing/test/RunMerge.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ def __init__(self):
self.newDQMIO = False
self.mergeNANO = False
self.bypassVersionCheck = False

self.isL1Scouting = False


def __call__(self):
if self.inputFiles == []:
Expand All @@ -39,7 +40,8 @@ def __call__(self):
output_lfn = self.outputLFN,
newDQMIO = self.newDQMIO,
mergeNANO = self.mergeNANO,
bypassVersionCheck = self.bypassVersionCheck)
bypassVersionCheck = self.bypassVersionCheck,
isL1Scouting = self.isL1Scouting)
except Exception as ex:
msg = "Error creating process for Merge:\n"
msg += str(ex)
Expand All @@ -55,8 +57,8 @@ def __call__(self):


if __name__ == '__main__':
valid = ["input-files=", "output-file=", "output-lfn=", "dqmroot", "mergeNANO", "bypassVersionCheck" ]
valid = ["input-files=", "output-file=", "output-lfn=", "dqmroot", "mergeNANO", "bypassVersionCheck", "isL1Scouting"]

usage = """RunMerge.py <options>"""
try:
opts, args = getopt.getopt(sys.argv[1:], "", valid)
Expand All @@ -83,5 +85,7 @@ def __call__(self):
merger.mergeNANO = True
if opt == "--bypassVersionCheck" :
merger.bypassVersionCheck = True
if opt == "--isL1Scouting" :
merger.isL1Scouting = True

merger()
20 changes: 20 additions & 0 deletions Configuration/DataProcessing/test/run_CfgTest_14.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/bin/bash

# Test suite for various ConfigDP scenarios
# run using: scram build runtests
# feel free to contribute with your favourite configuration


# Pass in name and status
function die { echo $1: status $2 ; exit $2; }

function runTest { echo $1 ; python3 $1 || die "Failure for configuration: $1" $?; }

declare -a arr=("l1ScoutingEra_Run3_2026")
for scenario in "${arr[@]}"
do
# RECO, AOD and DQMIO are not supported for L1-Scouting scenarios, and RunPromptReco.py is expected to ignore them without failing
runTest "${SCRAM_TEST_PATH}/RunPromptReco.py --scenario $scenario --nanoaod --global-tag GLOBALTAG --lfn=/store/whatever --nanoFlavours=@L1Scout --reco --aod --dqmio"
runTest "${SCRAM_TEST_PATH}/RunPromptReco.py --scenario $scenario --nanoaod --global-tag GLOBALTAG --lfn=/store/whatever --nanoFlavours=@L1Scout"
runTest "${SCRAM_TEST_PATH}/RunPromptReco.py --scenario $scenario --nanoaod --global-tag GLOBALTAG --lfn=/store/whatever --nanoFlavours=@L1ScoutSelect"
done
3 changes: 2 additions & 1 deletion Configuration/Eras/python/Era_Run3_2026_cff.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import FWCore.ParameterSet.Config as cms

from Configuration.Eras.Era_Run3_2025_cff import Run3_2025
from Configuration.Eras.Modifier_run3_l1scouting_2026_cff import run3_l1scouting_2026
from Configuration.ProcessModifiers.PixelCPEGeneric_cff import PixelCPEGeneric

Run3_2026 = cms.ModifierChain(Run3_2025, PixelCPEGeneric)
Run3_2026 = cms.ModifierChain(Run3_2025, PixelCPEGeneric, run3_l1scouting_2026)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import FWCore.ParameterSet.Config as cms

run3_l1scouting_2026 = cms.Modifier()
3 changes: 2 additions & 1 deletion Configuration/EventContent/python/EventContent_cff.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,11 @@
from DQMOffline.Configuration.DQMOffline_EventContent_cff import *
#
#
# NANOAOD
# NANOAOD (incl. the NANO(EDM)AOD event contents for Run-3 L1-Scouting data)
#
#
from PhysicsTools.NanoAOD.NanoAODEDMEventContent_cff import *
from PhysicsTools.NanoAOD.L1SCOUTNanoAODEDMEventContent_cff import *
#
#
# FastSim
Expand Down
54 changes: 53 additions & 1 deletion Configuration/PyReleaseValidation/python/relval_nano.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ def next(self, index: int = None) -> None:


################################################################
# current release cycle workflows : 14.0
# 14.0 workflows
steps['TTbarMINIAOD14.0'] = {'INPUT': InputInfo(
location='STD', dataSet='/RelValTTbar_14TeV/CMSSW_14_0_0-PU_140X_mcRun3_2024_realistic_v3_STD_2024_PU-v2/MINIAODSIM')}

Expand Down Expand Up @@ -432,6 +432,12 @@ def next(self, index: int = None) -> None:
steps['ScoutingPFMonitor_Run2025C_MINIAOD_150X'] = {'INPUT': InputInfo(
location='STD', ls=lumis_Run2025C, dataSet='/ScoutingPFMonitor/Run2025C-PromptReco-v1/MINIAOD')}

steps['L1Scouting2025RAW15.0'] = {'INPUT': InputInfo(location='STD', ls={398860: [[498, 498]]},
dataSet='/L1Scouting/Run2025G-v1/L1SCOUT')}

steps['L1ScoutingSelection2025RAW15.0'] = {'INPUT': InputInfo(location='STD', ls={398860: [[498, 498]]},
dataSet='/L1ScoutingSelection/Run2025G-v1/L1SCOUT')}

steps['NANO_data15.0'] = merge([{'--era': 'Run3_2025', '--conditions': 'auto:run3_data_prompt'}, _NANO_data])

steps['NANO_data15.0_prompt'] = merge([{'-s': 'NANO:@Prompt,DQM:@nanoAODDQM', '-n': '1000'},
Expand Down Expand Up @@ -467,6 +473,29 @@ def next(self, index: int = None) -> None:
steps['scoutingNANO_monitorWithPrompt_data15.0'] = merge([{'-s': 'NANO:@Prompt+@ScoutMonitor'},
steps['NANO_data15.0']])

steps['l1ScoutingNANO_data15.0'] = merge([{'-s': 'NANO:@L1Scout', '-n': '1000'},
steps['NANO_data15.0']])

steps['l1ScoutingSelectionNANO_data15.0'] = merge([{'-s': 'NANO:@L1ScoutSelect', '-n': '1000'},
steps['NANO_data15.0']])

################################################################
# Run-3, 16_0_X (2026 data-taking)

steps['L1Scouting2026RAW16.0'] = {'INPUT': InputInfo(location='STD', ls={402144: [[100, 100]]},
dataSet='/L1Scouting/Run2026B-v1/L1SCOUT')}

steps['L1ScoutingSelection2026RAW16.0'] = {'INPUT': InputInfo(location='STD', ls={402144: [[100, 100]]},
dataSet='/L1ScoutingSelection/Run2026B-v1/L1SCOUT')}

steps['NANO_data16.0'] = merge([{'--era': 'Run3_2026', '--conditions': 'auto:run3_data_prompt'}, _NANO_data])

steps['l1ScoutingNANO_data16.0'] = merge([{'-s': 'NANO:@L1Scout', '-n': '1000'},
steps['NANO_data16.0']])

steps['l1ScoutingSelectionNANO_data16.0'] = merge([{'-s': 'NANO:@L1ScoutSelect', '-n': '1000'},
steps['NANO_data16.0']])

################################################################
# NANOGEN
steps['NANOGENFromGen'] = merge([{'-s': 'NANO:@GEN,DQM:@nanogenDQM',
Expand Down Expand Up @@ -634,13 +663,36 @@ def next(self, index: int = None) -> None:
workflows[_wfn()] = ['ScoutingNANOmonitordata150Xrun3', ['ScoutingPFMonitor_Run2025C_MINIAOD_150X', 'scoutingNANO_monitor_data15.0']] # noqa
workflows[_wfn()] = ['ScoutingNANOmonitorWithPromptdata150Xrun3', ['ScoutingPFMonitor_Run2025C_MINIAOD_150X', 'scoutingNANO_monitorWithPrompt_data15.0']] # noqa
workflows[_wfn()] = ['BPHNANOdata150Xrun3', ['JetMET1_Run2025C_MINIAOD_150X', 'BPHNANO_data15.0']]
workflows[_wfn()] = ['L1ScoutingNANOdata150Xrun3', ['L1Scouting2025RAW15.0', 'l1ScoutingNANO_data15.0']]
workflows[_wfn()] = ['L1ScoutingSelectionNANOdata150Xrun3', ['L1ScoutingSelection2025RAW15.0', 'l1ScoutingSelectionNANO_data15.0']]

# DPG custom NANOs, data
_wfn.subnext()

# DPG custom NANOs, MC
_wfn.subnext()

_wfn.next(4)
######## 2500.4xxx ########
# Run3, 16_0_X input (2026 data-taking)
# Standard NANO, MC

# Standard NANO, data
_wfn.subnext()

# POG/PAG custom NANOs, MC
_wfn.subnext()

# POG/PAG custom NANOs, data
_wfn.subnext()
workflows[_wfn()] = ['L1ScoutingNANOdata160Xrun3', ['L1Scouting2026RAW16.0', 'l1ScoutingNANO_data16.0']]
workflows[_wfn()] = ['L1ScoutingSelectionNANOdata160Xrun3', ['L1ScoutingSelection2026RAW16.0', 'l1ScoutingSelectionNANO_data16.0']]

# DPG custom NANOs, data
_wfn.subnext()

# DPG custom NANOs, MC
_wfn.subnext()

_wfn.next(9)
######## 2500.9xxx ########
Expand Down
Loading