Exemplo n.º 1
0
def get_all_final_recoilers(reduced_process, excluded=()):

    model = reduced_process.get('model')
    return sub.SubtractionLegSet([
        leg for leg in reduced_process.get('legs')
        if all([leg['state'] == leg.FINAL, leg['number'] not in excluded])
    ])
Exemplo n.º 2
0
def get_recoilers(reduced_process, excluded=()):

    model = reduced_process.get('model')
    return sub.SubtractionLegSet([
        leg for leg in reduced_process.get('legs') if all([
            model.get_particle(leg['id']).get('mass').upper() == 'ZERO',
            leg['state'] == leg.FINAL, leg['number'] not in excluded
        ])
    ])
Exemplo n.º 3
0
    def map_leg_numbers_in_singular_structure(self, singular_structure,
                                              parents):
        """ Recursively walk through the singular structure and map the generic subtraction leg numbers to either:
        - the explicit indices of the resovled process
        - the explicit indices of the unresolved process
        - dummy intermediate indices
        """

        new_subtraction_legs = []
        for leg in singular_structure.legs:
            new_subtraction_legs.append(
                sub.SubtractionLeg(self.map_leg_number(leg.n, parents),
                                   leg.pdg, leg.state))
        singular_structure.legs = sub.SubtractionLegSet(new_subtraction_legs)
        for ss in singular_structure.substructures:
            self.map_leg_numbers_in_singular_structure(ss, parents)
Exemplo n.º 4
0
class QCD_S_FqFqx_C_FqFqx_C_IqpFqFqx(currents.GeneralQCDLocalCurrent):
    """ Nested soft FF (q_qx) limit within collinear FF (q_qx) limit with collinear limit IFF (q' q_qx)."""

    squared_orders = {'QCD': 4}
    n_loops = 0
    divide_by_jacobian = colorful_pp_config.divide_by_jacobian

    # In order to build the IF variables using the initial parent momentum which is absent from any mapping
    # structure, we use the global variables
    variables = staticmethod(QCD_S_FqFqx_C_FqFqx_C_IqpFqFqx_global_IFF_softFF_variables)

    # Now define the matching singular structures
    sub_coll_structure = sub.CollStructure(
        substructures=tuple([]),
        legs=(
            sub.SubtractionLeg(10, +2, sub.SubtractionLeg.FINAL),
            sub.SubtractionLeg(11, -2, sub.SubtractionLeg.FINAL),
        )
    )
    soft_structure = sub.SoftStructure(
            substructures=(sub_coll_structure,),
            legs=tuple([])
    )
    # This counterterm will be used if any of the structures of the list below matches
    structure = [
        # Match the case of the initial state being a quark and/or antiquark
        sub.SingularStructure(substructures=(sub.CollStructure(
            substructures=(soft_structure,),
            legs=(sub.SubtractionLeg(1, +1, sub.SubtractionLeg.INITIAL),)
        ),)),
    ]

    # An now the mapping rules
    mapping_rules = [
        {
            'singular_structure'    : sub.SingularStructure(substructures=(sub.CollStructure(
                substructures=tuple([]),
                legs=(
                    sub.SubtractionLeg(10, +2, sub.SubtractionLeg.FINAL),
                    sub.SubtractionLeg(11, -2, sub.SubtractionLeg.FINAL),
                )
            ),)),
            'mapping'               : colorful_pp_config.final_coll_mapping,
            # Intermediate legs should be strictly superior to a 1000
            'momenta_dict'          : bidict({1001:frozenset((10,11))}),
            'variables'             : currents.CompoundVariables(kernel_variables.colorful_pp_FFn_variables),
            'is_cut'                : colorful_pp_config.generalised_cuts,
            'reduced_recoilers'     : colorful_pp_config.get_initial_state_recoilers,
            'additional_recoilers'  : sub.SubtractionLegSet([sub.SubtractionLeg(1, +1, sub.SubtractionLeg.INITIAL),]),
        },
        {
            'singular_structure': sub.SingularStructure(substructures=(sub.SoftStructure(
                substructures=tuple([]),
                legs=(
                    sub.SubtractionLeg(1001, 21, sub.SubtractionLeg.FINAL),
                )
            ),)),
            'mapping'               : colorful_pp_config.soft_mapping,
            # -1 indicates that this ID should be replaced by the first overall parent connecting to the ME
            # The momenta dictionary below is irrelevant for the soft_mapping used above will make sure that
            # it applies the necessary relabelling of the final-state leg 33 to the parent -1 which will be
            # used by the reduced ME called with it.
            'momenta_dict'          : bidict({-1: frozenset((1,))}),
            'variables'             : None,
            'is_cut'                : colorful_pp_config.generalised_cuts,
            'reduced_recoilers'     : colorful_pp_config.get_final_state_recoilers,
            'additional_recoilers'  : sub.SubtractionLegSet([]),
        },
    ]

    def kernel(self, evaluation, all_steps_info, global_variables):
        """ Evaluate this I(FF) counterterm given the supplied variables. """

        kT_FF = all_steps_info[0]['variables'][0]['kTs'][(0,(1,))]
        z_FF  = all_steps_info[0]['variables'][0]['zs'][0]
        s_rs  = all_steps_info[0]['variables'][0]['ss'][(0,1)]

        kT_IF = global_variables['kTs'][0]
        x_IF  = global_variables['xs'][0]
        s_a_rs = global_variables['ss'][(0,1)]

        p_a_tilde = global_variables['p_a_tilde']

        p_rs_hat = all_steps_info[0]['lower_PS_point'][
            all_steps_info[0]['bundles_info'][0]['parent']
        ]

#        misc.sprint(s_rs,s_a_rs)
#        misc.sprint(p_a_tilde,p_rs_hat,p_a_tilde.dot(p_rs_hat))
#        misc.sprint(p_a_tilde,kT_FF,p_a_tilde.dot(kT_FF))
#        misc.sprint(kT_FF, kT_FF.square())
#        misc.sprint(x_IF)
#        misc.sprint(z_FF,(1.-z_FF))
        evaluation['values'][(0,0,0)] = EpsilonExpansion({'finite':
            (2./(s_rs*s_a_rs))*self.TR*self.CF*(
                1./(1.-x_IF) + z_FF * (1. - z_FF) * ((2.*p_a_tilde.dot(kT_FF))**2)/(kT_FF.square()*(2.*p_a_tilde.dot(p_rs_hat)))
            )
        })
        return evaluation
Exemplo n.º 5
0
class QCD_S_FqFqx_C_FqFqx(currents.GeneralQCDLocalCurrent):
    """ Nested soft FF (q_qx) limit within collinear FF (q_qx) limit."""

    squared_orders = {'QCD': 4}
    n_loops = 0
    divide_by_jacobian = colorful_pp_config.divide_by_jacobian

    # We should not need global variables for this current
    variables = None

    # Now define the matching singular structures
    sub_coll_structure = sub.CollStructure(
        substructures=tuple([]),
        legs=(
            sub.SubtractionLeg(10, +2, sub.SubtractionLeg.FINAL),
            sub.SubtractionLeg(11, -2, sub.SubtractionLeg.FINAL),
        )
    )
    # This counterterm will be used if any of the structures of the list below matches
    structure = [
        # Match both the case of the initial state being a quark and/or antiquark
        sub.SingularStructure(substructures=(sub.SoftStructure(
            substructures=(sub_coll_structure,),
            legs=tuple([])
        ),)),
    ]

    # An now the mapping rules
    mapping_rules = [
        {
            'singular_structure'    : sub.SingularStructure(substructures=(sub.CollStructure(
                substructures=tuple([]),
                legs=(
                    sub.SubtractionLeg(10, +2, sub.SubtractionLeg.FINAL),
                    sub.SubtractionLeg(11, -2, sub.SubtractionLeg.FINAL),
                )
            ),)),
            'mapping'               : colorful_pp_config.final_coll_mapping,
            # Intermediate legs should be strictly superior to a 1000
            'momenta_dict'          : bidict({1001:frozenset((10,11))}),
            'variables'             : currents.CompoundVariables(kernel_variables.colorful_pp_FFn_variables),
            'is_cut'                : colorful_pp_config.generalised_cuts,
            'reduced_recoilers'     : colorful_pp_config.get_initial_state_recoilers,
            'additional_recoilers'  : sub.SubtractionLegSet([]),
        },
        {
            'singular_structure': sub.SingularStructure(substructures=(sub.SoftStructure(
                substructures=tuple([]),
                legs=(
                    sub.SubtractionLeg(1001, 21, sub.SubtractionLeg.FINAL),
                )
            ),)),
            'mapping'               : colorful_pp_config.soft_mapping,
            # -1 indicates that this ID should be replaced by the first overall parent connecting to the ME
            'momenta_dict'          : bidict({}),
            'variables'             : None,
            'is_cut'                : colorful_pp_config.generalised_cuts,
            'reduced_recoilers'     : colorful_pp_config.get_final_state_recoilers,
            'additional_recoilers'  : sub.SubtractionLegSet([]),
        },
    ]

    def kernel(self, evaluation, all_steps_info, global_variables):
        """ Evaluate this I(FF) counterterm given the supplied variables. """

        kT_FF = all_steps_info[0]['variables'][0]['kTs'][(0,(1,))]
        z_FF  = all_steps_info[0]['variables'][0]['zs'][0]
        s_rs  = all_steps_info[0]['variables'][0]['ss'][(0,1)]

        parent = all_steps_info[0]['bundles_info'][0]['parent']

        prefactor = 1./s_rs

        for spin_correlation_vector, weight in AltarelliParisiKernels.P_qq(self, z_FF, kT_FF):
            complete_weight = weight * prefactor
            if spin_correlation_vector is None:
                evaluation['values'][(0, 0, 0)] = {'finite': complete_weight[0]}
            else:
                evaluation['spin_correlations'].append( ( (parent, (spin_correlation_vector,) ), ) )
                evaluation['values'][(len(evaluation['spin_correlations']) - 1, 0, 0)] = {'finite': complete_weight[0]}

        return evaluation

    def soft_kernel(self, evaluation, colored_partons, all_steps_info, global_variables):
        """Evaluate a collinear type of splitting kernel, which *does* need to know about the reduced process
        Should be specialised by the daughter class if not dummy
        """

        new_evaluation = utils.SubtractionCurrentEvaluation({
            'spin_correlations' : [ None, ],
            'color_correlations': [ ],
            'reduced_kinematics': evaluation['reduced_kinematics'],
            'values': { }
        })

        overall_lower_PS_point = all_steps_info[-1]['lower_PS_point']
        soft_leg_number = all_steps_info[-1]['bundles_info'][0]['final_state_children'][0]
        pr = all_steps_info[-1]['higher_PS_point'][soft_leg_number]
        colored_parton_numbers = sorted(colored_partons.keys())

        for i, a in enumerate(colored_parton_numbers):
            for b in colored_parton_numbers[i:]:
                # Write the eikonal for that pair
                if a!=b:
                    mult_factor = 1.
                else:
                    mult_factor = 1./2.

                pi = overall_lower_PS_point[a]
                pk = overall_lower_PS_point[b]
                composite_weight = EpsilonExpansion({'finite': 0.})
                for (sc, cc, rk), coll_weight in evaluation['values'].items():
                    if evaluation['spin_correlations'][sc] is None:
                        # We *subtract* here the contribution because the non-spin-correlated contributin is -g^{\mu\nu}
                        composite_weight -= SoftKernels.eikonal_g(self, pi, pk, pr, spin_corr_vector=None)*EpsilonExpansion(coll_weight)
                    else:
                        # Normally the collinear current should have built spin-correlations with the leg number corresponding
                        # to the soft one in the context of this soft current
                        assert len(evaluation['spin_correlations'][sc])==1
                        parent_number, spin_corr_vecs = evaluation['spin_correlations'][sc][0]
                        assert soft_leg_number==parent_number
                        assert len(spin_corr_vecs)==1
                        spin_corr_vec = spin_corr_vecs[0]
                        composite_weight += SoftKernels.eikonal_g(self, pi, pk, pr, spin_corr_vector=spin_corr_vec)*EpsilonExpansion(coll_weight)
                new_evaluation['color_correlations'].append( ((a, b), ) )
                new_evaluation['values'][(0,len(new_evaluation['color_correlations'])-1,0)] = composite_weight*mult_factor

        return new_evaluation
Exemplo n.º 6
0
class QCD_C_FqFqx_C_IqpFqFqx(currents.GeneralQCDLocalCurrent):
    """ Nested FF (q_qx) collinear within IFF (q_q'q'x)."""

    squared_orders = {'QCD': 4}
    n_loops = 0
    divide_by_jacobian = colorful_pp_config.divide_by_jacobian

    # We should not need global variables for this current
    variables = None

    # Now define the matching singular structures
    sub_coll_structure = sub.CollStructure(
        substructures=tuple([]),
        legs=(
            sub.SubtractionLeg(10, +2, sub.SubtractionLeg.FINAL),
            sub.SubtractionLeg(11, -2, sub.SubtractionLeg.FINAL),
        )
    )
    # This counterterm will be used if any of the structures of the list below matches
    structure = [
        # Match both the case of the initial state being a quark and/or antiquark
        sub.SingularStructure(substructures=(sub.CollStructure(
            substructures=(sub_coll_structure,),
            legs=(sub.SubtractionLeg(1, +1, sub.SubtractionLeg.INITIAL),)
        ),)),
    ]

    # An now the mapping rules
    mapping_rules = [
        {
            'singular_structure'    : sub.SingularStructure(substructures=(sub.CollStructure(
                substructures=tuple([]),
                legs=(
                    sub.SubtractionLeg(10, +2, sub.SubtractionLeg.FINAL),
                    sub.SubtractionLeg(11, -2, sub.SubtractionLeg.FINAL),
                )
            ),)),
            'mapping'               : colorful_pp_config.final_coll_mapping,
            # Intermediate legs should be strictly superior to a 1000
            'momenta_dict'          : bidict({1001:frozenset((10,11))}),
            'variables'             : currents.CompoundVariables(kernel_variables.colorful_pp_FFn_variables),
            'is_cut'                : colorful_pp_config.generalised_cuts,
            'reduced_recoilers'     : colorful_pp_config.get_initial_state_recoilers,
            'additional_recoilers'  : sub.SubtractionLegSet([sub.SubtractionLeg(1, +1, sub.SubtractionLeg.INITIAL)]),
        },
        {
            'singular_structure': sub.SingularStructure(substructures=(sub.CollStructure(
                substructures=tuple([]),
                legs=(
                    sub.SubtractionLeg(1, +1, sub.SubtractionLeg.INITIAL),
                    sub.SubtractionLeg(1001, 21, sub.SubtractionLeg.FINAL),
                )
            ),)),
            'mapping'               : colorful_pp_config.initial_coll_mapping,
            # -1 indicates that this ID should be replaced by the first overall parent connecting to the ME
            'momenta_dict'          : bidict({-1: frozenset((1001, 1))}),
            'variables'             : currents.CompoundVariables(kernel_variables.colorful_pp_IFn_variables),
            'is_cut'                : colorful_pp_config.generalised_cuts,
            'reduced_recoilers'     : colorful_pp_config.get_final_state_recoilers,
            'additional_recoilers'  : sub.SubtractionLegSet([]),
        },
    ]

    def kernel(self, evaluation, all_steps_info, global_variables):
        """ Evaluate this I(FF) counterterm given the supplied variables. """

        kT_FF = all_steps_info[0]['variables'][0]['kTs'][(0,(1,))]
        z_FF  = all_steps_info[0]['variables'][0]['zs'][0]
        s_rs  = all_steps_info[0]['variables'][0]['ss'][(0,1)]

        kT_IF = all_steps_info[1]['variables'][0]['kTs'][0]
        x_IF  = all_steps_info[1]['variables'][0]['xs'][0]
        s_a_rs = all_steps_info[1]['variables'][0]['ss'][(0,1)]

        p_a_hat = all_steps_info[1]['higher_PS_point'][
            all_steps_info[1]['bundles_info'][0]['initial_state_children'][0]
        ]
        p_rs_hat = all_steps_info[1]['higher_PS_point'][
            all_steps_info[1]['bundles_info'][0]['final_state_children'][0]
        ]

        parent = all_steps_info[1]['bundles_info'][0]['parent']

        #misc.sprint(s_rs, s_a_rs)
        #misc.sprint(z_FF, x_IF)
        #misc.sprint(kT_FF, kT_IF)
        #misc.sprint(p_a_hat, parent)

        # We must include here propagator factors, but no correction for symmetry
        # or averaging factor is necessary in this particular pure-quark kernel
        initial_state_crossing = -1.0
        prefactor = initial_state_crossing*(1./(s_rs*s_a_rs))
        for spin_correlation_vector, weight in AltarelliParisiKernels.P_q_qpqp(self, z_FF, 1./x_IF, kT_FF, -p_a_hat, p_rs_hat):
            complete_weight = weight * prefactor
            if spin_correlation_vector is None:
                evaluation['values'][(0, 0, 0)] = {'finite': complete_weight[0]}
            else:
                evaluation['spin_correlations'].append((parent, spin_correlation_vector))
                evaluation['values'][(len(evaluation['spin_correlations']) - 1, 0, 0)] = {'finite': complete_weight[0]}

        return evaluation
Exemplo n.º 7
0
    def evaluate_subtraction_current(self,
                                     current,
                                     higher_PS_point=None,
                                     momenta_dict=None,
                                     reduced_process=None,
                                     hel_config=None,
                                     Q=None,
                                     **opts):

        if higher_PS_point is None:
            raise CurrentImplementationError(
                self.name() + " needs the higher phase-space point.")
        if momenta_dict is None:
            raise CurrentImplementationError(self.name() +
                                             " requires a momenta dictionary.")
        if reduced_process is None:
            raise CurrentImplementationError(self.name() +
                                             " requires a reduced_process.")
        if not hel_config is None:
            raise CurrentImplementationError(
                self.name() + " does not support helicity assignment.")
        if Q is None:
            raise CurrentImplementationError(
                self.name() +
                " requires specification of the total incoming momentum Q.")

        # Retrieve alpha_s and mu_r
        model_param_dict = self.model.get('parameter_dict')
        alpha_s = model_param_dict['aS']
        mu_r = model_param_dict['MU_R']

        # Retrieve leg numbers
        overall_children = [
        ]  # the legs becoming unresolved in the resolved process (momenta in the real-emission ME)
        overall_parents = [
        ]  # the parent legs in the top-level reduced process (momenta in the mapped ME of this CT)
        # We take one defining structure (self.structure[0]). Its top-level substructures are the independent bundles
        # eg (C(1,2),C(4,5),S(6)).
        # Using structure[0] as the defining structure is fine for the purpose below since they should
        # all use the same leg numbers
        for bundle in self.structure[0].substructures:
            overall_children.append(
                tuple(self.leg_numbers_map[l.n]
                      for l in bundle.get_all_legs()))
            if self.has_parent(bundle, len(overall_children[-1])):
                overall_parents.append(
                    self.get_parent(frozenset(overall_children[-1]),
                                    momenta_dict))
            else:
                overall_parents.append(None)

        all_steps = [
            {
                'higher_PS_point': higher_PS_point
            },
        ]
        overall_jacobian = 1.
        for i_step, mapping_information in enumerate(self.mapping_rules):
            # Now obtain recoilers
            reduced_recoilers = mapping_information['reduced_recoilers'](
                reduced_process, excluded=tuple(overall_parents))
            additional_recoilers = sub.SubtractionLegSet(
                SubtractionLeg(self.map_leg_number(l.n, overall_parents),
                               l.pdg, l.state)
                for l in mapping_information['additional_recoilers'])
            all_recoilers = sub.SubtractionLegSet(
                list(reduced_recoilers) + list(additional_recoilers))

            # Now recursively apply leg numbers mappings
            mapping_singular_structure = mapping_information[
                'singular_structure'].get_copy()
            self.map_leg_numbers_in_singular_structure(
                mapping_singular_structure, overall_parents)
            # Now assign the recoilers (whose leg numbers have already been mapped)
            mapping_singular_structure.legs = all_recoilers

            # Build the momenta_dict by also substituting leg numbers
            this_momenta_dict = bidict({
                self.map_leg_number(k, overall_parents):
                frozenset([self.map_leg_number(n, overall_parents) for n in v])
                for k, v in mapping_information['momenta_dict'].items()
            })

            lower_PS_point, mapping_vars = mapping_information[
                'mapping'].map_to_lower_multiplicity(
                    all_steps[-1]['higher_PS_point'],
                    mapping_singular_structure,
                    this_momenta_dict,
                    compute_jacobian=self.divide_by_jacobian)
            if mappings.RelabellingMapping.needs_relabelling(
                    this_momenta_dict):
                lower_PS_point = mappings.RelabellingMapping.map_to_lower_multiplicity(
                    lower_PS_point, None, this_momenta_dict)

            all_steps[-1]['lower_PS_point'] = lower_PS_point
            # Q is provided externally
            mapping_vars.pop('Q', None)
            all_steps[-1]['mapping_vars'] = mapping_vars

            overall_jacobian *= mapping_vars.get('jacobian', 1.)

            bundles_info = []
            for bundle in mapping_singular_structure.substructures:
                bundles_info.append({})
                all_legs = bundle.get_all_legs()
                # This sorting is important so that the variables generated can be related to the legs specified
                # in the mapping singular structures of the mapping rules
                all_initial_legs = sorted(
                    [l for l in all_legs if l.state == l.INITIAL],
                    key=lambda l: l.n)
                all_final_legs = sorted(
                    [l for l in all_legs if l.state == l.FINAL],
                    key=lambda l: l.n)
                bundles_info[-1]['initial_state_children'] = tuple(
                    l.n for l in all_initial_legs)
                bundles_info[-1]['final_state_children'] = tuple(
                    l.n for l in all_final_legs)
                if self.has_parent(bundle, len(all_legs)):
                    bundles_info[-1]['parent'] = self.get_parent(
                        frozenset(l.n for l in all_legs), this_momenta_dict)
                else:
                    bundles_info[-1]['parent'] = None

                # Retrieve kinematics
                bundles_info[-1]['cut_inputs'] = {}
                if bundle.name() == 'C':
                    if len(all_initial_legs) > 0:
                        bundles_info[-1]['cut_inputs']['pA'] = -sum(
                            all_steps[-1]['higher_PS_point'][l.n]
                            for l in all_initial_legs)
                    bundles_info[-1]['cut_inputs']['pC'] = sum(
                        all_steps[-1]['higher_PS_point'][l.n]
                        for l in all_final_legs)
                elif bundle.name() == 'S':
                    bundles_info[-1]['cut_inputs']['pS'] = sum(
                        all_steps[-1]['higher_PS_point'][l.n]
                        for l in all_final_legs)

            all_steps[-1]['bundles_info'] = bundles_info

            # Get all variables for this level
            if mapping_information['variables'] is not None:
                all_steps[-1]['variables'] = mapping_information['variables'](
                    all_steps[-1]['higher_PS_point'],
                    all_steps[-1]['lower_PS_point'],
                    bundles_info,
                    Q=Q,
                    **mapping_vars)
            else:
                all_steps[-1]['variables'] = {}

            # Add the next higher PS point for the next level if necessary
            if i_step < (len(self.mapping_rules) - 1):
                all_steps.append({'higher_PS_point': lower_PS_point})

        overall_lower_PS_point = all_steps[-1]['lower_PS_point']
        reduced_kinematics = (None, overall_lower_PS_point)

        global_variables = {
            'overall_children': overall_children,
            'overall_parents': overall_parents,
            'leg_numbers_map': self.leg_numbers_map,
            'Q': Q,
        }
        # Build global variables if necessary
        if self.variables is not None:
            global_variables.update(self.variables(all_steps,
                                                   global_variables))

        # Apply cuts: include the counterterm only in a part of the phase-space
        for i_step, step_info in enumerate(all_steps):
            cut_inputs = dict(step_info)
            if self.mapping_rules[i_step]['is_cut'](cut_inputs,
                                                    global_variables):
                return utils.SubtractionCurrentResult.zero(
                    current=current,
                    hel_config=hel_config,
                    reduced_kinematics=('IS_CUT', overall_lower_PS_point))

#        for i_step, step_info in enumerate(all_steps):
#            misc.sprint("Higher PS point at step #%d: %s"%(i_step, str(step_info['higher_PS_point'])))
#            misc.sprint("Lower PS point at step  #%d: %s"%(i_step, str(step_info['lower_PS_point'])))

# Evaluate kernel
        evaluation = utils.SubtractionCurrentEvaluation({
            'spin_correlations': [
                None,
            ],
            'color_correlations': [
                None,
            ],
            'reduced_kinematics': [
                reduced_kinematics,
            ],
            'values': {}
        })

        # Apply collinear kernel (can be dummy)
        evaluation = self.kernel(evaluation, all_steps, global_variables)

        # Apply soft kernel (can be dummy), which also knows about the reduced process
        evaluation = self.call_soft_kernel(evaluation, reduced_process,
                                           all_steps, global_variables)

        # Add the normalization factors
        # WARNING! In this implementation the propagator denominators must be included in the kernel evaluation.
        norm = (8. * math.pi * alpha_s)**(self.squared_orders['QCD'] / 2)
        norm /= overall_jacobian
        for k in evaluation['values']:
            for term in evaluation['values'][k]:
                evaluation['values'][k][term] *= norm

        # Construct and return result
        result = utils.SubtractionCurrentResult()
        result.add_result(evaluation,
                          hel_config=hel_config,
                          squared_orders=tuple(
                              sorted(current.get('squared_orders').items())))
        return result