From cc3c95fe31b21f268dcf5ceee626fb3d92c8423b Mon Sep 17 00:00:00 2001
From: Eric Torrence <eric.torrence@cern.ch>
Date: Thu, 21 Apr 2022 05:47:26 +0200
Subject: [PATCH] Waveform updates

---
 CablingDB/cablingDB.py               |  61 ++++++++++++++-
 CablingDB/extractDigitizerMapping.py |  48 +++++++++++-
 CablingDB/extractDigitizerScale.py   | 107 +++++++++++++++++++++++++++
 3 files changed, 212 insertions(+), 4 deletions(-)
 create mode 100755 CablingDB/extractDigitizerScale.py

diff --git a/CablingDB/cablingDB.py b/CablingDB/cablingDB.py
index 50a742f..07757ea 100755
--- a/CablingDB/cablingDB.py
+++ b/CablingDB/cablingDB.py
@@ -528,10 +528,46 @@ digi_map = {
         ('preshower', 0, 0, 0),
         ('preshower', 0, 1, 0),
         ('none', 0),
+        ('clock', 0)],
+ 6525: [('calo', 0, 1, 0),
+        ('calo', 0, 0, 0),
+        ('calo', 1, 1, 0),
+        ('calo', 1, 0, 0),
+        ('vetonu', 0, 0, 0),
+        ('vetonu', 0, 1, 0),
+        ('veto', 1, 0, 0),
+        ('veto', 1, 1, 0),
+        ('timing', 0, 0, 1),
+        ('timing', 0, 0, 0),
+        ('timing', 0, 1, 1),
+        ('timing', 0, 1, 0),
+        ('preshower', 0, 0, 0),
+        ('preshower', 0, 1, 0),
+        ('veto', 0, 1, 0),
         ('clock', 0)]
 }
 
 
+# Range map from extractDigitizerScale.py
+range_map = {
+ 432: {0: 2.0,
+       1: 2.0,
+       2: 2.0,
+       3: 2.0,
+       4: 2.0,
+       5: 2.0,
+       6: 2.0,
+       7: 2.0,
+       8: 2.0,
+       9: 2.0,
+       10: 2.0,
+       11: 2.0,
+       12: 2.0,
+       13: 2.0,
+       14: 2.0,
+       15: 2.0}
+}
+
 # Look for data entry errors
 
 print('Validating TRB data')
@@ -573,7 +609,7 @@ for run, data in digi_map.items():
         assert isinstance(data[i][1], int), 'Digitizer station is not int'
         assert isinstance(data[i][2], int), 'Digitizer plane is not int'
         assert isinstance(data[i][3], int), 'Digitizer pmt is not int'
-        assert data[i][0] in ['trigger', 'veto', 'preshower', 'calo'], 'Invalid type'
+        assert data[i][0] in ['trigger', 'timing', 'veto', 'vetonu', 'preshower', 'calo'], 'Invalid type'
         assert data[i][1] >= -1 and data[i][1] <= 1, 'Invalid station number'
         assert data[i][2] >= -1 and data[i][2] <= 3, 'Invalid plate number'
         assert data[i][3] >= -1 and data[i][3] <= 1, 'Invalid pmt number'
@@ -635,7 +671,8 @@ for firstValidRun, mapping in reversed(digi_map.items()):
             cableRecord['row'] = mapping[channel][1]
             cableRecord['module'] = mapping[channel][2]
             cableRecord['pmt'] = mapping[channel][3]
-        elif cableRecord['type'] in ['trigger', 'veto', 'preshower']:
+        # Already asserted allowable types above
+        elif len(mapping[channel]) == 4:
             cableRecord['station'] = mapping[channel][1]
             cableRecord['plate'] = mapping[channel][2]
             cableRecord['pmt'] = mapping[channel][3]
@@ -644,7 +681,27 @@ for firstValidRun, mapping in reversed(digi_map.items()):
 
     lastValid = ((firstValidRun - 1) << 32) | (cool.ValidityKeyMax & 0x00000000FFFFFFFF)
 
+# Add digitizer scale
+print('Recreating digitizer scale')
+
+digiSpec = cool.RecordSpecification()
+digiSpec.extend('range', cool.StorageType.Float)
+
+digiFolderSpec = cool.FolderSpecification(cool.FolderVersioning.SINGLE_VERSION, digiSpec)
+digiFolder = db.createFolder('/WAVE/DAQ/Range', digiFolderSpec, description, True)
+
+# There should be one record entered per IOV
+lastValid = cool.ValidityKeyMax
+for firstValidRun, mapping in reversed(range_map.items()):
+    firstValid = (firstValidRun << 32)
+    for channel in range(16):
+        digiRecord = cool.Record(digiSpec)
+        digiRecord['range'] = mapping[channel]
+        digiFolder.storeObject( firstValid, lastValid, digiRecord, int(channel) )
+
+    lastValid = ((firstValidRun - 1) << 32) | (cool.ValidityKeyMax & 0x00000000FFFFFFFF)
 
+# Done
 db.closeDatabase()
 
 print('Database completed')
diff --git a/CablingDB/extractDigitizerMapping.py b/CablingDB/extractDigitizerMapping.py
index 9619462..a5278d9 100755
--- a/CablingDB/extractDigitizerMapping.py
+++ b/CablingDB/extractDigitizerMapping.py
@@ -5,6 +5,21 @@ import copy
 import pprint
 import sys
 import requests
+import argparse
+
+# Parse any command-line options
+parser = argparse.ArgumentParser(description="Digitizer Mapping Checker")
+
+parser.add_argument("-r", "--firstRun", default=0,
+                    help="Specify first run to consider (default: all)")
+
+args = parser.parse_args()
+try:
+    first_run = int(args.firstRun)
+except Exception as e:
+    print("Error parsing first run:")
+    print(e)
+    sys.exit(-1)
 
 # This only works on lxplus
 response = requests.get("http://faser-runnumber.web.cern.ch/RunList")
@@ -16,6 +31,7 @@ digitizer_map={}
 # Calo: (row, mod, pmt)
 # Veto/Preshower/Trigger: (station, plate, pmt)
 
+# Orignal TI12 detector, without FASERnu veto
 TI12_map = [
     ('calo', 0, 1, 0),  # Digitizer 0
     ('calo', 0, 0, 0),
@@ -35,6 +51,28 @@ TI12_map = [
     ('clock', 0) # Digitizer 15
 ] 
 
+# TI12 map as of run 6520, when the FASERnu veto is installed
+# Installed 3/15/2022, first non-test run 6525
+# Also, rename trigger to timing
+TI12_6520_map = [
+    ('calo', 0, 1, 0),  # Digitizer 0
+    ('calo', 0, 0, 0),
+    ('calo', 1, 1, 0),
+    ('calo', 1, 0, 0),
+    ('vetonu', 0, 0, 0),  # Digitizer 4
+    ('vetonu', 0, 1, 0),
+    ('veto', 1, 0, 0),     # Digitizer 6
+    ('veto', 1, 1, 0),
+    ('timing', 0, 0, 1),  # Digitizer 8
+    ('timing', 0, 0, 0), 
+    ('timing', 0, 1, 1), 
+    ('timing', 0, 1, 0), 
+    ('preshower', 0, 0, 0), # Digitizer 12
+    ('preshower', 0, 1, 0),
+    ('veto', 0, 1, 0), # Digitizer 14
+    ('clock', 0) # Digitizer 15
+] 
+
 TB_map = [
     ('calo', 1, 2, 0),  # Digitizer 0
     ('calo', 0, 2, 0),
@@ -55,12 +93,15 @@ TB_map = [
 ]
 
 for info in runs:
+
+    runno=info["runnumber"]
+    if runno < first_run: continue
+
     # Skip runtypes we don't want to reconstruct
     runtype = info['type']
     if "Calibration" in runtype: continue
     if runtype == "Test": continue
 
-    runno=info["runnumber"]
     detectors=info["detectors"]
     digs=[det for det in detectors if det.startswith("DIG")]
     if not digs: continue
@@ -147,7 +188,10 @@ for info in runs:
             print(f"ERROR: TI12 run {runno} with unusal number of TRBs: {trbIDs}")
             sys.exit(1)
 
-        digitizer_map[runno] = copy.copy(TI12_map)
+        if runno < 6520:
+            digitizer_map[runno] = copy.copy(TI12_map)
+        else:
+            digitizer_map[runno] = copy.copy(TI12_6520_map)
 
     # Remove any channels not enabled in readout
     first = True
diff --git a/CablingDB/extractDigitizerScale.py b/CablingDB/extractDigitizerScale.py
new file mode 100755
index 0000000..27a182c
--- /dev/null
+++ b/CablingDB/extractDigitizerScale.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+
+import json
+import copy
+import pprint
+import sys
+import requests
+import argparse
+
+# Parse any command-line options
+parser = argparse.ArgumentParser(description="Digitizer Scale Checker")
+
+parser.add_argument("-r", "--firstRun", default=0,
+                    help="Specify first run to consider (default: all)")
+
+args = parser.parse_args()
+try:
+    first_run = int(args.firstRun)
+except Exception as e:
+    print("Error parsing first run:")
+    print(e)
+    sys.exit(-1)
+
+# This only works on lxplus
+response = requests.get("http://faser-runnumber.web.cern.ch/RunList")
+runs = response.json()
+runs.reverse()
+
+# Keyed by run number, contains map of channels => ranges
+range_map = dict()
+prev_map = None
+
+for info in runs:
+
+    runno=info["runnumber"]
+    if runno < first_run: continue
+
+    # Skip runtypes we don't want to reconstruct
+    runtype = info['type']
+    if "Calibration" in runtype: continue
+    if runtype == "Test": continue
+
+    detectors=info["detectors"]
+    digs=[det for det in detectors if det.startswith("DIG")]
+    if not digs: continue
+    trbs=[det for det in detectors if det.startswith("TRB")]
+    if not trbs: continue
+    trbIDs=[int(trb[3:]) for trb in trbs]
+    trbIDs.sort()
+    host=info["host"].split(".")[0]
+
+    # Check if host makes sense
+    if not host in ["faser-daq-002","faser-daq-006","faser-daq-009","faser-daq-010"]:
+        print(f"ERROR: run {runno} is running on unusual host {host} - not on list of runs to ignore")
+        sys.exit(1)
+
+    # Find the readout channels
+    query = f"https://faser-runinfo.app.cern.ch/cgibin/getRunInfo.py?runno={runno}"
+    response = requests.get(query)
+    config = response.json().get('configuration', None)
+
+    if not config:
+        print(f"ERROR: run {runno} has no configuration!")
+        sys.exit(1)
+
+    comps = config['components']
+    digi_channels = []
+    dynamic_range = dict()
+
+    for comp in comps:
+        if comp['name'] != "digitizer01": continue
+
+        if not comp.get('modules', None):
+
+            if not comp.get('settings', None):
+                print(f"ERROR run {runno} has no modules or settings!")
+                sys.exit(1)
+
+            else:
+                dig = comp['settings']
+                readout = dig['channel_readout']
+
+        else:
+            dig = comp['modules'][0]
+            if dig['name'] != "digitizer01": continue
+            settings = dig['settings']
+            readout = settings['channel_readout']
+
+        for chan in readout:
+            chid = int(chan['channel'])
+            digi_channels.append(chid)
+            dynamic_range[chid] = float(chan['dynamic_range'])
+
+        break
+
+    # Done filling dynamic_range map
+
+    if prev_map is None or prev_map != dynamic_range:
+        range_map[runno] = copy.copy(dynamic_range)
+        prev_map = range_map[runno]
+
+pprint.pprint(range_map)
+#print(digitizer_map)
+print(f"Entries: {len(range_map)} runs")
+
+#FIXME: should add check that last run always have full TI12 detector map future runs...
+
-- 
GitLab