CMS 3D CMS Logo

l1tGTSingleInOutLUT.py
Go to the documentation of this file.
1 """
2 This computes the most optimal COS_PHI_LUT and COSH_ETA_LUT. Call
3 :func:`~l1tGTSingleInOutLUT.SingleInOutLUT.export` to export the
4 generated LUT.
5 """
6 
7 import FWCore.ParameterSet.Config as cms
8 from L1Trigger.Phase2L1GT.l1tGTScales import scale_parameter
9 from statistics import mean, median, stdev
10 import math
11 
12 
14 
15  def __init__(self, width_in, unused_lsbs, lsb, output_scale_factor, operation, start_value=0, label="", width_out_force=0):
16  self.debug_txt = ""
17  input_scale_factor = 2**unused_lsbs * lsb
18  self.unused_lsbs = unused_lsbs
19  self.lsb = lsb
20  signed_output = min([operation(input_scale_factor * (i + 0.5) + start_value)
21  for i in range(2**width_in)]) < 0
22 
23  if width_out_force == 0:
24  self.width_out = math.ceil(math.log2(output_scale_factor *
25  max([abs(operation(input_scale_factor * (i + 0.5) + start_value)) for i in range(2**width_in - 1)] +
26  [abs(operation(input_scale_factor * (2**width_in - 1) + start_value))])))
27  if signed_output:
28  self.width_out += 1
29  else:
30  self.width_out = width_out_force
31 
32  self.debug_info(
33  "***************************** {} LUT {} *****************************".format(operation.__name__, label))
34  self.debug_info("Depth: {} x {} (addr x data)".format(width_in, self.width_out))
35  self.debug_info("Scale: {}".format(output_scale_factor))
36 
37  self.width_in = width_in
38  self.output_scale_factor = output_scale_factor
39  self.input_scale_factor = input_scale_factor
40  self.operation = operation
41  self.start_value = start_value
42  self.lut = cms.vint32(
43  * ([min(round(output_scale_factor * operation(input_scale_factor * (i + 0.5) + start_value)), 2**self.width_out - 1) for i in range(2**width_in - 1)]
44  + [min(round(output_scale_factor * operation(input_scale_factor * (2 ** width_in - 1) + start_value)), 2**self.width_out - 1)]))
45 
46  self.print_error()
47 
48  def debug_info(self, msg):
49  self.debug_txt += msg + "\n"
50 
51  def config(self):
52  return cms.PSet(
53  output_scale_factor=cms.double(self.output_scale_factor),
54  unused_lsbs=cms.uint32(self.unused_lsbs),
55  lut=self.lut,
56  max_error=cms.double(self.max_error)
57  )
58 
59  def export(self, filename: str):
60  print(self.debug_txt)
61  with open(filename, "w") as file:
62  for value in self.lut:
63  file.write("{:X}".format(int(value) & ((1 << self.width_out) - 1)
64  ).rjust(math.ceil(self.width_out/4), '0') + "\n")
65 
66  @ staticmethod
67  def optimal_scale_factor(width_in, max_width_out, unused_lsbs, lsb, operation, start_value=0):
68  input_scale_factor = 2**unused_lsbs * lsb
69  scale_factor = (2**max_width_out - 1) / max([abs(operation(input_scale_factor * (i + 0.5) + start_value))
70  for i in range(2**width_in)])
71  return scale_factor
72 
73  def print_error(self):
74  errors = [abs(self.lut[int(i/(2**self.unused_lsbs))]/self.output_scale_factor -
75  self.operation(i * self.lsb + self.start_value)) for i in range(2**(self.width_in + self.unused_lsbs))]
76 
77  self.max_error = max(errors)
78 
79  self.debug_info("Error: {:.5f} +/- {:.5f}, max: {:.5f}, total: {:.5f}, median: {:.5f}".format(
80  mean(errors), stdev(errors), self.max_error, sum(errors), median(errors)))
81 
82  # mass_errors = [errors[i]/(2*self.operation(i * self.lsb + self.start_value)) for i in range(2**(self.width_in + self.unused_lsbs)) ]
83  # self.debug_info("inv mass error: {:.5f} +/- {:.5f}, max: {:.5f}, total: {:.5f}, median: {:.5f}".format(
84  # mean(mass_errors), stdev(mass_errors), max(mass_errors), sum(mass_errors), median(mass_errors)))
85 
86 
87 COS_PHI_IN_WIDTH = 10 # not using 2 lsb and 1 msb (cos(x + pi) = -cos(x), x in [0, pi))
88 COSH_ETA_IN_WIDTH = 11 # not using 2 lsb and 1 msb (splitted LUT)
89 ISOLATION_WIDTH = 11
90 
91 # Since we calculate cosh(dEta) - cos(dPhi); both must be on the same scale the difference should fit into 17 bits for the DSP
92 optimal_scale_factor_lower = math.floor(
93  (2**17 - 1) / (math.cosh((2**(COSH_ETA_IN_WIDTH + 2) - 1)*scale_parameter.eta_lsb.value()) + 1))
94 
95 COS_PHI_LUT = SingleInOutLUT(
96  COS_PHI_IN_WIDTH, 2, scale_parameter.phi_lsb.value(), optimal_scale_factor_lower, math.cos)
97 
98 # eta in [0, 2pi)
99 COSH_ETA_LUT = SingleInOutLUT(
100  COSH_ETA_IN_WIDTH, 2, scale_parameter.eta_lsb.value(), optimal_scale_factor_lower, math.cosh, 0, "[0, 2pi)")
101 
102 optimal_scale_factor_upper = SingleInOutLUT.optimal_scale_factor(
103  COSH_ETA_IN_WIDTH, 17, 2, scale_parameter.eta_lsb.value(), math.cosh, 2**13 * scale_parameter.eta_lsb.value())
104 
105 # Ensure a bitshift between upper and lower scale factor (makes 3- and 4-body invariant mass calculations easier).
106 # As a result values for cosh(dEta) with approximately dEta > 12.5 are binned to a lower value.
107 # However, there is no detector at abs(eta) > 6, so it shouldn't matter.
108 optimal_scale_factor_upper = optimal_scale_factor_lower / \
109  math.pow(2, math.floor(math.log2(optimal_scale_factor_lower / optimal_scale_factor_upper)))
110 
111 # eta in [2pi, 4pi)
112 COSH_ETA_LUT_2 = SingleInOutLUT(
113  COSH_ETA_IN_WIDTH, 2, scale_parameter.eta_lsb.value(), optimal_scale_factor_upper, math.cosh, 2**13 * scale_parameter.eta_lsb.value(), "[2pi, 4pi)", 17)
def optimal_scale_factor(width_in, max_width_out, unused_lsbs, lsb, operation, start_value=0)
void print(TMatrixD &m, const char *label=nullptr, bool mathematicaFormat=false)
Definition: Utilities.cc:47
Abs< T >::type abs(const T &t)
Definition: Abs.h:22
def __init__(self, width_in, unused_lsbs, lsb, output_scale_factor, operation, start_value=0, label="", width_out_force=0)
T median(std::vector< T > values)
Definition: median.h:16
def stdev(xlist)
Definition: plotscripts.py:69