From 4573b716ec78a2bbd1348bc4a2a027d1d796cd2f Mon Sep 17 00:00:00 2001
From: Wouter Deconinck <wouter.deconinck@umanitoba.ca>
Date: Sun, 17 Oct 2021 18:45:50 +0000
Subject: [PATCH] CI job to automatically generate ACTS material scans

---
 .gitlab-ci.yml                                |   1 +
 benchmarks/material_maps/config.yml           |  22 ++++
 .../material_maps/scripts/configureMap.py     | 101 ++++++++++++++
 .../material_maps/scripts/writeMapConfig.py   | 123 ++++++++++++++++++
 4 files changed, 247 insertions(+)
 create mode 100644 benchmarks/material_maps/config.yml
 create mode 100644 benchmarks/material_maps/scripts/configureMap.py
 create mode 100644 benchmarks/material_maps/scripts/writeMapConfig.py

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 25d1fa03..7b56a51f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -110,6 +110,7 @@ include:
   - local: 'benchmarks/roman_pots/config.yml'
   - local: 'benchmarks/zdc/config.yml'
   - local: 'benchmarks/crystal_calorimeter/config.yml'
+  - local: 'benchmarks/material_maps/config.yml'
   - local: 'benchmarks/pid/config.yml'
   - local: 'benchmarks/timing/config.yml'
   - local: 'benchmarks/b0_tracker/config.yml'
diff --git a/benchmarks/material_maps/config.yml b/benchmarks/material_maps/config.yml
new file mode 100644
index 00000000..0d1104d1
--- /dev/null
+++ b/benchmarks/material_maps/config.yml
@@ -0,0 +1,22 @@
+material_maps:
+  extends: .det_benchmark
+  stage: simulate
+  script:
+    - ActsExampleGeometryDD4hep -j 1 -n 1 --dd4hep-input ${DETECTOR_PATH}/${JUGGLER_DETECTOR}.xml --output-json --output-root --mat-output-file geometry-map --mat-output-allmaterial true --mat-output-sensitives false
+    - test -f geometry-map.json
+    - python3 benchmarks/material_maps/scripts/writeMapConfig.py geometry-map.json config-map.json
+    - test -f config-map.json
+    - python3 benchmarks/material_maps/scripts/configureMap.py geometry-map.json config-map.json
+    - test -f config-map.json
+    - ActsExampleMaterialRecordingDD4hep -j 1 -n ${JUGGLER_N_EVENTS} --dd4hep-input ${DETECTOR_PATH}/${JUGGLER_DETECTOR}.xml --output-root 
+    - test -f geant4_material_tracks.root
+    - root -l -b -q -e "TChain T(\"material-tracks\"); T.Add(\"geant4_material_tracks.root\"); cout << T.GetEntries() << \" entries\" << endl; gApplication->Terminate(T.GetEntries()-${JUGGLER_N_EVENTS});"
+    - ActsExampleMaterialMappingDD4hep -j 1 -n ${JUGGLER_N_EVENTS} --dd4hep-input ${DETECTOR_PATH}/${JUGGLER_DETECTOR}.xml --input-root true --input-files geant4_material_tracks.root --mat-input-type file --mat-input-file geometry-map.json --output-root --output-json --output-cbor --mat-output-file material-maps --mat-mapping-surfaces true --mat-mapping-volumes true --mat-mapping-volume-stepsize 1
+    - test -f material-maps.json
+    - ActsExampleMaterialValidationDD4hep -j 1 -n ${JUGGLER_N_EVENTS} --dd4hep-input ${DETECTOR_PATH}/${JUGGLER_DETECTOR}.xml --mat-input-type file --mat-input-file material-maps.json --output-root --mat-output-file val-mat-map --prop-z0-sigma 0.0 --prop-d0-sigma 0.0
+    - ls -al
+    - mkdir -p results/material_maps
+    - cp *.root *.json *.cbor results/material_maps/
+  artifacts:
+    paths:
+      - results/material_maps
diff --git a/benchmarks/material_maps/scripts/configureMap.py b/benchmarks/material_maps/scripts/configureMap.py
new file mode 100644
index 00000000..1ebf729d
--- /dev/null
+++ b/benchmarks/material_maps/scripts/configureMap.py
@@ -0,0 +1,101 @@
+# This file is part of the Acts project.
+#
+# Copyright (C) 2020-2021 CERN for the benefit of the Acts project
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import json
+import sys
+
+# Should be run with Python 3 if possible
+# Script that use the json config file to configure the Json surfaces map for the material mapping
+# Take two arguments in input : The path to the surfaces map and the path of the json config file
+# By default the input is : 'surfaces-map.json' and the output is : 'config-map.json'
+# The config file can be used to define a binning for all the surfaces in a given volume
+# It can also be used to define the binning for volume mapping
+
+if sys.version_info[0] < 3:
+    print('Using Python 2')
+    print('To obtain the proper ordering in the Json files Python 3 is recomanded')
+
+if len(sys.argv) < 2 :
+    inFileName = 'geometry-maps.json'
+    confFileName = 'config-map.json'
+    
+if len(sys.argv) < 3 :
+    confFileName = 'config-map.json'
+    
+else :
+    inFileName = sys.argv[1]
+    confFileName = sys.argv[2]
+
+    
+with open(inFileName,'r+') as json_file:
+    with open(confFileName,'r') as config_file:
+
+        config = json.load(config_file)
+        data = json.load(json_file)
+
+        for entry in data['Surfaces']['entries']:
+
+            if 'type' not in entry['value']['bounds']:
+                entry['value']['bounds']['type'] = ''
+
+            if 'layer' in entry:  
+                if 'approach' not in entry:
+                    if 'sensitive' not in entry:
+                        for conf in config['Surfaces'][str(entry['volume'])]:
+                            if 'layer' in conf and conf['layer'] == 'X' and conf['value']['bounds']['type'] == entry['value']['bounds']['type']:
+                                entry['value']['material']['mapMaterial'] = conf['value']['material']['mapMaterial']
+                                entry['value']['material']['mappingType'] = conf['value']['material']['mappingType']
+                                ibin = 0
+                                for bin in entry['value']['material']['binUtility']['binningdata']:                                  
+                                    bin['bins'] = conf['value']['material']['binUtility']['binningdata'][ibin]['bins']
+                                    ibin = ibin+1
+                                continue
+                        continue
+
+            if 'boundary' in entry:    
+                if 'layer' not in entry:
+                    for conf in config['Surfaces'][str(entry['volume'])]:
+                        if 'boundary' in conf and conf['boundary'] == entry['boundary'] and conf['value']['bounds']['type'] == entry['value']['bounds']['type']:
+                            entry['value']['material']['mapMaterial'] = conf['value']['material']['mapMaterial']
+                            entry['value']['material']['mappingType'] = conf['value']['material']['mappingType']
+                            ibin = 0
+                            for bin in entry['value']['material']['binUtility']['binningdata']:
+                                bin['bins'] = conf['value']['material']['binUtility']['binningdata'][ibin]['bins']
+                                ibin = ibin+1
+                            continue
+                    continue
+                 
+            if 'approach' in entry:
+                if 'sensitive' not in entry:
+                    for conf in config['Surfaces'][str(entry['volume'])]:
+                        if 'approach' in conf and conf['approach'] == entry['approach'] and conf['value']['bounds']['type'] == entry['value']['bounds']['type']:
+                            entry['value']['material']['mapMaterial'] = conf['value']['material']['mapMaterial']
+                            entry['value']['material']['mappingType'] = conf['value']['material']['mappingType']
+                            ibin = 0
+                            for bin in entry['value']['material']['binUtility']['binningdata']:
+                                bin['bins'] = conf['value']['material']['binUtility']['binningdata'][ibin]['bins']
+                                ibin = ibin+1
+                            continue
+                    continue
+                 
+            if 'sensitive' in entry:  
+                if 'approach' not in entry:
+                    for conf in config['Surfaces'][str(entry['volume'])]:
+                        if 'sensitive' in conf and conf['sensitive'] == 'X' and conf['layer'] == entry['layer'] and conf['value']['bounds']['type'] == entry['value']['bounds']['type']:
+                            entry['value']['material']['mapMaterial'] = conf['value']['material']['mapMaterial']
+                            entry['value']['material']['mappingType'] = conf['value']['material']['mappingType']
+                            ibin = 0
+                            for bin in entry['value']['material']['binUtility']['binningdata']:
+                                bin['bins'] = conf['value']['material']['binUtility']['binningdata'][ibin]['bins']
+                                ibin = ibin+1
+                            continue
+                    continue  
+        data['Volumes'] = config['Volumes']        
+    json_file.seek(0) 
+    json.dump(data, json_file, indent=4)
+    json_file.truncate()
diff --git a/benchmarks/material_maps/scripts/writeMapConfig.py b/benchmarks/material_maps/scripts/writeMapConfig.py
new file mode 100644
index 00000000..6c32a22d
--- /dev/null
+++ b/benchmarks/material_maps/scripts/writeMapConfig.py
@@ -0,0 +1,123 @@
+# This file is part of the Acts project.
+#
+# Copyright (C) 2020-2021 CERN for the benefit of the Acts project
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import json
+import sys
+
+# Should be run with Python 3 if possible
+# Script that parse a Json surfaces map to create an easy to use json config file for the mapping
+# Take two arguments in input : The path to the surfaces map and the path of the json config file
+# By default the input is : 'surfaces-map.json' and the output is : 'config-map.json'
+# The config file can be used to define a binning for all the surfaces in a given volume
+# It can also be used to define the binning for volume mapping
+
+def getSurfaceMateral(mat):
+    outputmat = {}
+    value = {}
+    material = {}
+    bound = {}
+    outputmat['volume'] = mat['volume']
+    if 'boundary' in mat:
+        outputmat['boundary'] = mat['boundary']
+    if 'layer' in mat:
+        if 'approach' not in entry:
+            if 'sensitive' not in entry:        
+                outputmat['layer'] = 'X'
+    if 'approach' in mat:
+        outputmat['approach'] = mat['approach']
+    if 'sensitive' in mat:
+        outputmat['layer'] = mat['layer']
+        outputmat['sensitive'] = 'X'
+    material['binUtility'] = mat['value']['material']['binUtility']
+    material['mapMaterial'] = False
+    material['mappingType'] = mat['value']['material']['type']
+    bound['type'] = mat['value']['bounds']['type']
+    value['material'] = material
+    value['bounds'] = bound
+    outputmat['value'] = value
+    return outputmat
+
+if sys.version_info[0] < 3:
+    print('Using Python 2')
+    print('To obtain the proper ordering in the Json files Python 3 is recomanded')
+
+if len(sys.argv) < 2 :
+    inFileName = 'geometry-maps.json'
+else :
+    inFileName = sys.argv[1]
+
+    
+with open(inFileName,'r') as json_file:
+    config = {}
+    config['Surfaces'] = {}
+    data = json.load(json_file)
+    lastVol = -1
+    for entry in data['Surfaces']['entries']:
+        if lastVol != entry['volume']:
+            if lastVol != -1:
+                config['Surfaces'][lastVol] = vconfig
+            vconfig = []
+            lastVol = entry['volume']
+            typeLayer = []
+            createdApproach1 = False
+            createdApproach2 = False
+            typeSensitive = {}
+
+        if 'type' not in entry['value']['bounds']:
+            entry['value']['bounds']['type'] = ''
+
+        if 'layer' in entry:  
+            if 'approach' not in entry:
+                if 'sensitive' not in entry:
+                    if entry['value']['bounds']['type'] not in typeLayer:
+                        typeLayer.append(entry['value']['bounds']['type'])
+                        surface = getSurfaceMateral(entry)
+                        vconfig.append(surface)
+                        continue
+
+        if 'boundary' in entry:    
+            if 'layer' not in entry:
+                surface = getSurfaceMateral(entry)
+                vconfig.append(surface)
+                continue         
+
+        if 'approach' in entry:
+            if 'sensitive' not in entry:
+                if entry['approach'] == 1 and createdApproach1 == False:
+                    createdApproach1 = True
+                    surface = getSurfaceMateral(entry)
+                    vconfig.append(surface)
+                    continue
+                if entry['approach'] == 2 and createdApproach2 == False:
+                    createdApproach2 = True
+                    surface = getSurfaceMateral(entry)
+                    vconfig.append(surface)
+                    continue
+
+        if 'sensitive' in entry:  
+            if 'approach' not in entry:
+                if entry['value']['material']['binUtility']['binningdata'] != None: 
+                    if not entry['layer'] in typeSensitive:
+                        typeSensitive[entry['layer']]=[]
+                    if entry['value']['bounds']['type'] not in typeSensitive[entry['layer']]:
+                        typeSensitive[entry['layer']].append(entry['value']['bounds']['type'])
+                        surface = getSurfaceMateral(entry)
+                        vconfig.append(surface)
+                        continue
+
+    if lastVol != -1:
+        config['Surfaces'][lastVol] = vconfig
+    config['Volumes'] = data['Volumes']
+
+if len(sys.argv) < 3 :
+    outFileName = 'config-map.json'
+else :
+    outFileName = sys.argv[2]
+    
+with open(outFileName, 'w') as outfile:
+    json.dump(config, outfile, indent=4)
-- 
GitLab