Repository: dmwm/wmcore
Issue: Override of Multicore/EventStreams in Resubmission of StepChains not functional

**Impact of the bug**
ReqMgr2

**Describe the bug**
StepChain workflows have some specialized mapping of step name and the cmsRun number, such that arguments can be properly mapped and updated accordingly.

When it comes to the Resubmission factory, that specific map is not created, which causes a tweak of Multicore/EventStreams not to be properly applied to the relevant cmsRun steps. Instead, those values are applied to the whole task (affecting all cmsRun steps in it).

**How to reproduce it**
Create an ACDC for a StepChain workflow, and provide a dictionary value for the `Multicore` parameter during ACDC workflow creation.

**Expected behavior**
The expected behaviour for ACDCs of StepChain - with Multicore/EventStreams tweaks during the workflow creation - is:
* if those values are provided as a simple object (int/float), then that value should be applied to all the steps in a StepChain workflow (thus, all cmsRun steps would have the same value)
* if those values are provided as a dictionary object, key'ed by the StepName, then each step dictionary needs to be updated accordingly and only the relevant cmsRun step should be set with that value (thus, each cmsRun step can have a different value)

NOTE: if there is no Multicore/EventStreams override, such ACDCs are already fully functional.

**Additional context and error message**
Somehow related to https://github.com/dmwm/WMCore/issues/10741 and https://github.com/dmwm/WMCore/issues/10788

Example from the workload spec definition:
```
amaltaro_ACDC1_SC_PY3_PURecyc_HG2109_Val_210907_142258_2229.tasks.RecoPU_2021PU.steps.cmsRun1.application.section_('multicore')
amaltaro_ACDC1_SC_PY3_PURecyc_HG2109_Val_210907_142258_2229.tasks.RecoPU_2021PU.steps.cmsRun1.application.multicore.eventStreams = 9
amaltaro_ACDC1_SC_PY3_PURecyc_HG2109_Val_210907_142258_2229.tasks.RecoPU_2021PU.steps.cmsRun1.application.multicore.numberOfCores = 19
...
amaltaro_ACDC1_SC_PY3_PURecyc_HG2109_Val_210907_142258_2229.tasks.RecoPU_2021PU.steps.cmsRun1.tree.children.cmsRun2.application.section_('multicore')
amaltaro_ACDC1_SC_PY3_PURecyc_HG2109_Val_210907_142258_2229.tasks.RecoPU_2021PU.steps.cmsRun1.tree.children.cmsRun2.application.multicore.eventStreams = 9
amaltaro_ACDC1_SC_PY3_PURecyc_HG2109_Val_210907_142258_2229.tasks.RecoPU_2021PU.steps.cmsRun1.tree.children.cmsRun2.application.multicore.numberOfCores = 19
```

Changed files:
src/python/WMCore/WMSpec/StdSpecs/Resubmission.py, test/python/WMCore_t/WMSpec_t/StdSpecs_t/Resubmission_t.py

diff --git a/src/python/WMCore/WMSpec/StdSpecs/Resubmission.py b/src/python/WMCore/WMSpec/StdSpecs/Resubmission.py
index ab9136305..f6dc0ea72 100644
--- a/src/python/WMCore/WMSpec/StdSpecs/Resubmission.py
+++ b/src/python/WMCore/WMSpec/StdSpecs/Resubmission.py
@@ -45,7 +45,7 @@ class ResubmissionWorkloadFactory(StdBase):
         if "Memory" in self.userArgs:
             helper.setMemory(arguments["Memory"])
         if "Multicore" in self.userArgs or "EventStreams" in self.userArgs:
-            helper.setCoresAndStreams(arguments["Multicore"], arguments.get("EventStreams", 0))
+            self.setCoresAndStreams(helper, arguments)
         if "TimePerEvent" in self.userArgs:
             helper.setTimePerEvent(arguments.get("TimePerEvent"))
 
@@ -142,6 +142,48 @@ class ResubmissionWorkloadFactory(StdBase):
                 else:
                     arguments[innerKey][arg] = self.userArgs[arg]
 
+    def setCoresAndStreams(self, workloadHelper, inputArgs):
+        """
+        Set helper for the Multicore and EventStreams parameters, which
+        need to be dealt with in a different way depending on the parent
+        spec type
+        :param workloadHelper: WMWorkload object
+        :param inputArgs: dictionary with the Resubmission input args
+        """
+        # simple and easy way to update it
+        if not isinstance(inputArgs["Multicore"], dict):
+            workloadHelper.setCoresAndStreams(inputArgs["Multicore"], inputArgs.get("EventStreams", 0))
+        # still a simple way to update it
+        elif inputArgs['OriginalRequestType'] == "TaskChain":
+            workloadHelper.setCoresAndStreams(inputArgs["Multicore"], inputArgs.get("EventStreams", 0))
+        # check if it's a StepChain then based on its steps mapping
+        elif workloadHelper.getStepMapping():
+            # map is supposed to be in the format of:
+            # {'RecoPU_2021PU': ('Step1', 'cmsRun1'), 'Nano_2021PU': ('Step2', 'cmsRun2')}
+            stepChainMap = workloadHelper.getStepMapping()
+            # we need to create an easier map now
+            coresByCmsRun = {}
+            evtStreamsByCmsRun = {}
+            for stepName in stepChainMap:
+                cores = inputArgs["Multicore"][stepName]
+                coresByCmsRun[stepChainMap[stepName][1]] = cores
+                if inputArgs["EventStreams"] and isinstance(inputArgs["EventStreams"], dict):
+                    streams = inputArgs["EventStreams"][stepName]
+                elif inputArgs["EventStreams"]:
+                    streams = inputArgs["EventStreams"]
+                else:
+                    streams = 0
+                evtStreamsByCmsRun[stepChainMap[stepName][1]] = streams
+
+            # Now iterate through the tasks and update it from within the steps
+            for task in workloadHelper.taskIterator():
+                if task.taskType() in ["Merge", "Harvesting", "Cleanup", "LogCollect"]:
+                    continue
+                for cmsRunName in coresByCmsRun:
+                    stepHelper = task.getStepHelper(cmsRunName)
+                    stepHelper.setNumberOfCores(coresByCmsRun[cmsRunName],
+                                                evtStreamsByCmsRun[cmsRunName])
+
     def validateSchema(self, schema):
         """
         Since we skip the master validation for Resubmission specs, we better have
@@ -168,4 +210,4 @@ class ResubmissionWorkloadFactory(StdBase):
                     msg = "ACDC for parent spec of type: {} ".format(schema['OriginalRequestType'])
                     msg += "cannot have parameter: {} defined as a dictionary: {}".format(param,
                                                                                           schema[param])
-                    self.raiseValidationException(msg)
\\ No newline at end of file
+                    self.raiseValidationException(msg)
diff --git a/test/python/WMCore_t/WMSpec_t/StdSpecs_t/Resubmission_t.py b/test/python/WMCore_t/WMSpec_t/StdSpecs_t/Resubmission_t.py
index 879e3041a..1a17825c3 100644
--- a/test/python/WMCore_t/WMSpec_t/StdSpecs_t/Resubmission_t.py
+++ b/test/python/WMCore_t/WMSpec_t/StdSpecs_t/Resubmission_t.py
@@ -370,11 +370,12 @@ class ResubmissionTests(EmulatedUnitTestCase):
                 perfParams = taskObj.jobSplittingParameters()['performance']
                 print("Task: {}, type: {}, perf: {}".format(taskObj.name(), taskObj.taskType(), perfParams))
                 if taskObj.taskType() in ('Production', 'Processing'):
-                    for step in ('cmsRun1', 'cmsRun2'):
-                        stepHelper = taskObj.getStepHelper(step)
-                        # FIXME: these 2 lines below are wrong, see GH issue #10791
-                        self.assertEqual(stepHelper.getNumberOfCores(), acdcArgs["Multicore"]["RecoPU_2021PU"])
-                        self.assertEqual(stepHelper.getNumberOfStreams(), acdcArgs["EventStreams"]["RecoPU_2021PU"])
+                    stepHelper = taskObj.getStepHelper("cmsRun1")
+                    self.assertEqual(stepHelper.getNumberOfCores(), acdcArgs["Multicore"]["RecoPU_2021PU"])
+                    self.assertEqual(stepHelper.getNumberOfStreams(), acdcArgs["EventStreams"]["RecoPU_2021PU"])
+                    stepHelper = taskObj.getStepHelper("cmsRun2")
+                    self.assertEqual(stepHelper.getNumberOfCores(), acdcArgs["Multicore"]["Nano_2021PU"])
+                    self.assertEqual(stepHelper.getNumberOfStreams(), acdcArgs["EventStreams"]["Nano_2021PU"])
                     for step in ('stageOut1', 'logArch1'):
                         stepHelper = taskObj.getStepHelper(step)
                         self.assertEqual(stepHelper.getNumberOfCores(), 1)
@@ -436,7 +437,6 @@ class ResubmissionTests(EmulatedUnitTestCase):
                 if taskObj.taskType() in ('Production', 'Processing'):
                     for step in ('cmsRun1', 'cmsRun2'):
                         stepHelper = taskObj.getStepHelper(step)
-                        # FIXME: these 2 lines below are wrong, see GH issue #10791
                         self.assertEqual(stepHelper.getNumberOfCores(), acdcArgs["Multicore"])
                         self.assertEqual(stepHelper.getNumberOfStreams(), acdcArgs["EventStreams"])
                     for step in ('stageOut1', 'logArch1'):