From 6bcff344243ad3a653fddd0fe1edc18b69a84ef9 Mon Sep 17 00:00:00 2001
From: Andre Sailer <andre.philippe.sailer@cern.ch>
Date: Mon, 17 Jul 2023 17:46:52 +0200
Subject: [PATCH] DDSim: ConfigHelper: prevent using unknown properties

Everywhere but in Meta, where we want to allow adding arbitrary information
---
 DDG4/python/DDSim/DD4hepSimulation.py       |  1 +
 DDG4/python/DDSim/Helper/Action.py          |  1 +
 DDG4/python/DDSim/Helper/ConfigHelper.py    | 16 ++++++++++++++++
 DDG4/python/DDSim/Helper/Filter.py          |  1 +
 DDG4/python/DDSim/Helper/Geometry.py        |  1 +
 DDG4/python/DDSim/Helper/GuineaPig.py       |  1 +
 DDG4/python/DDSim/Helper/Gun.py             |  1 +
 DDG4/python/DDSim/Helper/HepMC3.py          |  1 +
 DDG4/python/DDSim/Helper/Input.py           |  2 +-
 DDG4/python/DDSim/Helper/InputConfig.py     |  1 +
 DDG4/python/DDSim/Helper/LCIO.py            |  1 +
 DDG4/python/DDSim/Helper/MagneticField.py   |  1 +
 DDG4/python/DDSim/Helper/Meta.py            |  1 +
 DDG4/python/DDSim/Helper/Output.py          |  1 +
 DDG4/python/DDSim/Helper/OutputConfig.py    |  1 +
 DDG4/python/DDSim/Helper/ParticleHandler.py |  1 +
 DDG4/python/DDSim/Helper/Physics.py         |  1 +
 DDG4/python/DDSim/Helper/Random.py          |  1 +
 DDG4/python/DDSim/Helper/UI.py              |  1 +
 19 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/DDG4/python/DDSim/DD4hepSimulation.py b/DDG4/python/DDSim/DD4hepSimulation.py
index f84f7b69c..b0af0d847 100644
--- a/DDG4/python/DDSim/DD4hepSimulation.py
+++ b/DDG4/python/DDSim/DD4hepSimulation.py
@@ -566,6 +566,7 @@ class DD4hepSimulation(object):
               self._errorMessages.append("ERROR: %s " % e)
               if logger.level <= logging.DEBUG:
                 self._errorMessages.append(traceback.format_exc())
+        obj._checkProperties()
 
   def __checkOutputLevel(self, level):
     """return outputlevel as int so we don't have to import anything for faster startup"""
diff --git a/DDG4/python/DDSim/Helper/Action.py b/DDG4/python/DDSim/Helper/Action.py
index 6f9f0bf41..b84c3f70d 100644
--- a/DDG4/python/DDSim/Helper/Action.py
+++ b/DDG4/python/DDSim/Helper/Action.py
@@ -39,6 +39,7 @@ class Action(ConfigHelper):
     self._mapActions = dict()
     self._trackerSDTypes = ['tracker']
     self._calorimeterSDTypes = ['calorimeter']
+    self._closeProperties()
 
   @property
   def tracker(self):
diff --git a/DDG4/python/DDSim/Helper/ConfigHelper.py b/DDG4/python/DDSim/Helper/ConfigHelper.py
index 9bcce56f7..35297b7cb 100644
--- a/DDG4/python/DDSim/Helper/ConfigHelper.py
+++ b/DDG4/python/DDSim/Helper/ConfigHelper.py
@@ -16,9 +16,25 @@ call for the parser object create an additional member::
 class ConfigHelper(object):
   """Base class for configuration helper"""
 
+  # We need a static set of properties, because steeringFile parsing creates new Helper objects!
+  _setOfProperties = dict()
+
   def __init__(self):
     pass
 
+  def _name(self):
+    return self.__class__.__name__
+
+  def _closeProperties(self):
+    """Store the list of properties."""
+    self._setOfProperties[self._name()] = set(vars(self))
+
+  def _checkProperties(self):
+    newProps = set(vars(self))
+    if existingProps := self._setOfProperties.get(self._name(), set()):
+      if unknownProps := newProps - existingProps:
+        raise RuntimeError(f"{self._name()} error: Trying to add unknown propert(y/ies): {unknownProps}!")
+
   def getOptions(self):
     finalVars = {}
 
diff --git a/DDG4/python/DDSim/Helper/Filter.py b/DDG4/python/DDSim/Helper/Filter.py
index 0b967904a..ce7c39317 100644
--- a/DDG4/python/DDSim/Helper/Filter.py
+++ b/DDG4/python/DDSim/Helper/Filter.py
@@ -40,6 +40,7 @@ class Filter(ConfigHelper):
     self._calo = "edep0"
     self._filters = {}
     self._createDefaultFilters()
+    self._closeProperties()
 
   @property
   def tracker(self):
diff --git a/DDG4/python/DDSim/Helper/Geometry.py b/DDG4/python/DDSim/Helper/Geometry.py
index 5718e4002..c7c7e85d2 100644
--- a/DDG4/python/DDSim/Helper/Geometry.py
+++ b/DDG4/python/DDSim/Helper/Geometry.py
@@ -35,6 +35,7 @@ class Geometry(ConfigHelper):
 
     self._dumpDGDML_EXTRA = {"help": "If not empty, filename to dump the Geometry as GDML"}
     self.dumpGDML = ""
+    self._closeProperties()
 
   def constructGeometry(self, kernel, geant4, geoPrintLevel=2, numberOfThreads=1):
     """Construct Geant4 geometry."""
diff --git a/DDG4/python/DDSim/Helper/GuineaPig.py b/DDG4/python/DDSim/Helper/GuineaPig.py
index 1e2d26976..e771c2970 100644
--- a/DDG4/python/DDSim/Helper/GuineaPig.py
+++ b/DDG4/python/DDSim/Helper/GuineaPig.py
@@ -9,6 +9,7 @@ class GuineaPig(Input):
   def __init__(self):
     super(GuineaPig, self).__init__()
     self._parameters["ParticlesPerEvent"] = -1
+    self._closeProperties()
 
   @property
   def particlesPerEvent(self):
diff --git a/DDG4/python/DDSim/Helper/Gun.py b/DDG4/python/DDSim/Helper/Gun.py
index 8b21d52ea..608b41f7c 100644
--- a/DDG4/python/DDSim/Helper/Gun.py
+++ b/DDG4/python/DDSim/Helper/Gun.py
@@ -43,6 +43,7 @@ class Gun(ConfigHelper):
                                             'eta', 'pseudorapidity',
                                             'ffbar']}  # (1+cos^2 theta)
     self._distribution = None
+    self._closeProperties()
 
   @property
   def distribution(self):
diff --git a/DDG4/python/DDSim/Helper/HepMC3.py b/DDG4/python/DDSim/Helper/HepMC3.py
index 95e360c74..078d36141 100644
--- a/DDG4/python/DDSim/Helper/HepMC3.py
+++ b/DDG4/python/DDSim/Helper/HepMC3.py
@@ -12,6 +12,7 @@ class HepMC3(Input):
     self._parameters["Flow2"] = "flow2"
     # this option will evaluate to True if the HEPMC3 plugin was build
     self._useHepMC3 = ("@DD4HEP_USE_HEPMC3@" != "OFF")
+    self._closeProperties()
 
   @property
   def useHepMC3(self):
diff --git a/DDG4/python/DDSim/Helper/Input.py b/DDG4/python/DDSim/Helper/Input.py
index 8154930ad..82288a63e 100644
--- a/DDG4/python/DDSim/Helper/Input.py
+++ b/DDG4/python/DDSim/Helper/Input.py
@@ -8,8 +8,8 @@ class Input(ConfigHelper):
 
   def __init__(self):
     super(Input, self).__init__()
-
     self.__parameters = {}
+    self._closeProperties()
 
   def getParameters(self):
     return self.__parameters
diff --git a/DDG4/python/DDSim/Helper/InputConfig.py b/DDG4/python/DDSim/Helper/InputConfig.py
index 3392f8c89..375cdc859 100644
--- a/DDG4/python/DDSim/Helper/InputConfig.py
+++ b/DDG4/python/DDSim/Helper/InputConfig.py
@@ -10,6 +10,7 @@ class InputConfig(ConfigHelper):
   def __init__(self):
     super(InputConfig, self).__init__()
     self._userPlugin = []
+    self._closeProperties()
 
   @property
   def userInputPlugin(self):
diff --git a/DDG4/python/DDSim/Helper/LCIO.py b/DDG4/python/DDSim/Helper/LCIO.py
index 7c0d6e849..79f789153 100644
--- a/DDG4/python/DDSim/Helper/LCIO.py
+++ b/DDG4/python/DDSim/Helper/LCIO.py
@@ -9,6 +9,7 @@ class LCIO(Input):
   def __init__(self):
     super(LCIO, self).__init__()
     self._parameters["MCParticleCollectionName"] = "MCParticle"
+    self._closeProperties()
 
   @property
   def mcParticleCollectionName(self):
diff --git a/DDG4/python/DDSim/Helper/MagneticField.py b/DDG4/python/DDSim/Helper/MagneticField.py
index dff5d51c9..f61bd60c5 100644
--- a/DDG4/python/DDSim/Helper/MagneticField.py
+++ b/DDG4/python/DDSim/Helper/MagneticField.py
@@ -17,3 +17,4 @@ class MagneticField(ConfigHelper):
     self.delta_intersection = 0.001 * mm
     self.delta_one_step = 0.01 * mm
     self.largest_step = 10 * m
+    self._closeProperties()
diff --git a/DDG4/python/DDSim/Helper/Meta.py b/DDG4/python/DDSim/Helper/Meta.py
index d1a367738..7b1e2f2e9 100644
--- a/DDG4/python/DDSim/Helper/Meta.py
+++ b/DDG4/python/DDSim/Helper/Meta.py
@@ -27,6 +27,7 @@ class Meta(ConfigHelper):
                                              " E.g setting it to 42 will start counting events from 42 instead of 0",
                                      'type': int}
     self.eventNumberOffset = 0
+    # no closeProperties, allow adding arbitrary information to runHeader
 
   def parseEventParameters(self):
     """
diff --git a/DDG4/python/DDSim/Helper/Output.py b/DDG4/python/DDSim/Helper/Output.py
index f0ffe32bf..035e353c9 100644
--- a/DDG4/python/DDSim/Helper/Output.py
+++ b/DDG4/python/DDSim/Helper/Output.py
@@ -52,6 +52,7 @@ class Output(ConfigHelper):
 
     self._geometry_EXTRA = {'choices': OUTPUT_CHOICES, 'type': outputLevelType}
     self._geometry = outputLevel('DEBUG')
+    self._closeProperties()
 
   @property
   def inputStage(self):
diff --git a/DDG4/python/DDSim/Helper/OutputConfig.py b/DDG4/python/DDSim/Helper/OutputConfig.py
index c4d05ea83..f16c12f8b 100644
--- a/DDG4/python/DDSim/Helper/OutputConfig.py
+++ b/DDG4/python/DDSim/Helper/OutputConfig.py
@@ -26,6 +26,7 @@ class OutputConfig(ConfigHelper):
     self._forceLCIO = False
     self._forceEDM4HEP = False
     self._forceDD4HEP = False
+    self._closeProperties()
 
   def _checkConsistency(self):
     """Raise error if more than one force flag is true."""
diff --git a/DDG4/python/DDSim/Helper/ParticleHandler.py b/DDG4/python/DDSim/Helper/ParticleHandler.py
index fb75d450f..9b7aafe42 100644
--- a/DDG4/python/DDSim/Helper/ParticleHandler.py
+++ b/DDG4/python/DDSim/Helper/ParticleHandler.py
@@ -19,6 +19,7 @@ class ParticleHandler(ConfigHelper):
     self._minDistToParentVertex = 2.2e-14 * mm
     self._enableDetailedHitsAndParticleInfo = False
     self._userParticleHandler = "Geant4TCUserParticleHandler"
+    self._closeProperties()
 
   @property
   def enableDetailedHitsAndParticleInfo(self):
diff --git a/DDG4/python/DDSim/Helper/Physics.py b/DDG4/python/DDSim/Helper/Physics.py
index 18bbded1e..ac92bd52d 100644
--- a/DDG4/python/DDSim/Helper/Physics.py
+++ b/DDG4/python/DDSim/Helper/Physics.py
@@ -27,6 +27,7 @@ class Physics(ConfigHelper):
                         5101, 5103, 5201, 5203, 5301, 5303, 5401, 5403, 5503}  # b? diquarks
     self._zeroTimePDGs = {11, 13, 15, 17}
     self._userFunctions = []
+    self._closeProperties()
 
   @property
   def rejectPDGs(self):
diff --git a/DDG4/python/DDSim/Helper/Random.py b/DDG4/python/DDSim/Helper/Random.py
index 363388e6f..5486c0211 100644
--- a/DDG4/python/DDSim/Helper/Random.py
+++ b/DDG4/python/DDSim/Helper/Random.py
@@ -23,6 +23,7 @@ class Random (ConfigHelper):
                                            "on eventID and runID\nAllows reproducibility even when"
                                            "SkippingEvents"}
     self.enableEventSeed = False
+    self._closeProperties()
 
   def initialize(self, DDG4, kernel, output):
     """ initialize the random generator
diff --git a/DDG4/python/DDSim/Helper/UI.py b/DDG4/python/DDSim/Helper/UI.py
index 053cb84f3..d405a444b 100644
--- a/DDG4/python/DDSim/Helper/UI.py
+++ b/DDG4/python/DDSim/Helper/UI.py
@@ -29,6 +29,7 @@ class UI(ConfigHelper):
     self._commandsPostRun = []
     self._commandsPreRun = []
     self._commandsTerminate = []
+    self._closeProperties()
 
   @property
   def commandsConfigure(self):
-- 
GitLab