Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-07-03 04:18:04

0001 """
0002 This computes the most optimal COS_PHI_LUT and COSH_ETA_LUT. Call
0003 :func:`~l1tGTSingleInOutLUT.SingleInOutLUT.export` to export the
0004 generated LUT.
0005 """
0006 
0007 import FWCore.ParameterSet.Config as cms
0008 from L1Trigger.Phase2L1GT.l1tGTScales import scale_parameter
0009 from statistics import mean, median, stdev
0010 import math
0011 
0012 
0013 class SingleInOutLUT:
0014 
0015     def __init__(self, width_in, unused_lsbs, lsb, output_scale_factor, operation, start_value=0, label="", width_out_force=0):
0016         self.debug_txt = ""
0017         input_scale_factor = 2**unused_lsbs * lsb
0018         self.unused_lsbs = unused_lsbs
0019         self.lsb = lsb
0020         signed_output = min([operation(input_scale_factor * (i + 0.5) + start_value)
0021                             for i in range(2**width_in)]) < 0
0022 
0023         if width_out_force == 0:
0024             self.width_out = math.ceil(math.log2(output_scale_factor *
0025                                                  max([abs(operation(input_scale_factor * (i + 0.5) + start_value)) for i in range(2**width_in - 1)] +
0026                                                      [abs(operation(input_scale_factor * (2**width_in - 1) + start_value))])))
0027             if signed_output:
0028                 self.width_out += 1
0029         else:
0030             self.width_out = width_out_force
0031 
0032         self.debug_info(
0033             "***************************** {} LUT {} *****************************".format(operation.__name__, label))
0034         self.debug_info("Depth: {} x {} (addr x data)".format(width_in, self.width_out))
0035         self.debug_info("Scale: {}".format(output_scale_factor))
0036 
0037         self.width_in = width_in
0038         self.output_scale_factor = output_scale_factor
0039         self.input_scale_factor = input_scale_factor
0040         self.operation = operation
0041         self.start_value = start_value
0042         self.lut = cms.vint32(
0043             * ([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)]
0044                + [min(round(output_scale_factor * operation(input_scale_factor * (2 ** width_in - 1) + start_value)), 2**self.width_out - 1)]))
0045 
0046         self.print_error()
0047 
0048     def debug_info(self, msg):
0049         self.debug_txt += msg + "\n"
0050 
0051     def config(self):
0052         return cms.PSet(
0053             output_scale_factor=cms.double(self.output_scale_factor),
0054             unused_lsbs=cms.uint32(self.unused_lsbs),
0055             lut=self.lut,
0056             max_error=cms.double(self.max_error)
0057         )
0058 
0059     def export(self, filename: str):
0060         print(self.debug_txt)
0061         with open(filename, "w") as file:
0062             for value in self.lut:
0063                 file.write("{:X}".format(int(value) & ((1 << self.width_out) - 1)
0064                                          ).rjust(math.ceil(self.width_out/4), '0') + "\n")
0065 
0066     @ staticmethod
0067     def optimal_scale_factor(width_in, max_width_out, unused_lsbs, lsb, operation, start_value=0):
0068         input_scale_factor = 2**unused_lsbs * lsb
0069         scale_factor = (2**max_width_out - 1) / max([abs(operation(input_scale_factor * (i + 0.5) + start_value))
0070                                                      for i in range(2**width_in)])
0071         return scale_factor
0072 
0073     def print_error(self):
0074         errors = [abs(self.lut[int(i/(2**self.unused_lsbs))]/self.output_scale_factor -
0075                       self.operation(i * self.lsb + self.start_value)) for i in range(2**(self.width_in + self.unused_lsbs))]
0076 
0077         self.max_error = max(errors)
0078 
0079         self.debug_info("Error: {:.5f} +/- {:.5f}, max: {:.5f}, total: {:.5f}, median: {:.5f}".format(
0080             mean(errors), stdev(errors), self.max_error, sum(errors), median(errors)))
0081 
0082         # mass_errors = [errors[i]/(2*self.operation(i * self.lsb + self.start_value)) for i in range(2**(self.width_in + self.unused_lsbs)) ]
0083         # self.debug_info("inv mass error: {:.5f} +/- {:.5f}, max: {:.5f}, total: {:.5f}, median: {:.5f}".format(
0084         #       mean(mass_errors), stdev(mass_errors), max(mass_errors), sum(mass_errors), median(mass_errors)))
0085 
0086 
0087 COS_PHI_IN_WIDTH = 10   # not using 2 lsb and 1 msb (cos(x + pi) = -cos(x), x in [0, pi))
0088 COSH_ETA_IN_WIDTH = 11  # not using 2 lsb and 1 msb (splitted LUT)
0089 ISOLATION_WIDTH = 11
0090 
0091 # Since we calculate cosh(dEta) - cos(dPhi); both must be on the same scale the difference should fit into 17 bits for the DSP
0092 optimal_scale_factor_lower = math.floor(
0093     (2**17 - 1) / (math.cosh((2**(COSH_ETA_IN_WIDTH + 2) - 1)*scale_parameter.eta_lsb.value()) + 1))
0094 
0095 COS_PHI_LUT = SingleInOutLUT(
0096     COS_PHI_IN_WIDTH, 2, scale_parameter.phi_lsb.value(), optimal_scale_factor_lower, math.cos)
0097 
0098 # eta in [0, 2pi)
0099 COSH_ETA_LUT = SingleInOutLUT(
0100     COSH_ETA_IN_WIDTH, 2, scale_parameter.eta_lsb.value(), optimal_scale_factor_lower, math.cosh, 0, "[0, 2pi)")
0101 
0102 optimal_scale_factor_upper = SingleInOutLUT.optimal_scale_factor(
0103     COSH_ETA_IN_WIDTH, 17, 2, scale_parameter.eta_lsb.value(), math.cosh, 2**13 * scale_parameter.eta_lsb.value())
0104 
0105 # Ensure a bitshift between upper and lower scale factor (makes 3- and 4-body invariant mass calculations easier).
0106 # As a result values for cosh(dEta) with approximately dEta > 12.5 are binned to a lower value.
0107 # However, there is no detector at abs(eta) > 6, so it shouldn't matter.
0108 optimal_scale_factor_upper = optimal_scale_factor_lower / \
0109     math.pow(2, math.floor(math.log2(optimal_scale_factor_lower / optimal_scale_factor_upper)))
0110 
0111 # eta in [2pi, 4pi)
0112 COSH_ETA_LUT_2 = SingleInOutLUT(
0113     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)