Skip to content

Phase-space bound misses the -sum-mass spelling of a pair cut #121

Description

@oliviermattelaer

Follow-up to #110, deliberately left out of that PR because it is a behaviour change rather than a rename. #110 can merge as it is.

The gap

lepton-lepton-sum-mass and lepton-pair_mass are the same cut: both are the invariant mass of every lepton pair. As event filters they behave identically. Only the second one bounds the integration.

Measuring the floor Cuts::m_inv_min() reports for a min=500 cut on the top pair of g g > t t~ g g:

cut floor
top-pair_mass 500.0
top-sum-mass 500.0
top-top-sum-mass 0.0

So a user who writes the cut the pre-existing way gets the forbidden region generated and thrown away, while the same cut spelled pair_mass never generates it. Nothing warns them; the cross section is right either way, only the efficiency differs.

Why

Cuts::m_inv_min() in madspace/src/phasespace/cuts.cpp:129 matches the summed spelling with

if (o.sum_momenta() && idx.size() == 1 && idx.at(0).size() == 2) {
    pairs.emplace_back(idx.at(0).at(0), idx.at(0).at(1));
}

Observable::build_indices lays out _indices as one list per selection group. A single group (top-sum-mass) gives idx.size() == 1 with both members inside idx[0], which is the shape above. Two groups (top-top-sum-mass) give idx.size() == 2, one member each, so it falls through and contributes no floor.

That second shape is exactly what the obs_pair_mass branch just below (cuts.cpp:137) already handles - it is keyed on the observable, so it never sees an obs_mass observable.

Reproducing

import madspace as ms
pids = [21, 21, 6, -6, 21, 21]
O = ms.Observable

def floor(o):
    return ms.Cuts([ms.CutItem(o, min=500.0)]).m_inv_min()[0][1]

print(floor(O(pids, 'pair_mass', [[6, -6]])))                          # 500.0
print(floor(O(pids, 'mass', [[6, -6]], sum_momenta=True)))             # 500.0
print(floor(O(pids, 'mass', [[6, -6], [6, -6]], sum_momenta=True)))    # 0.0

Fix

Extend the summed lambda to also accept idx.size() == 2 with sum_momenta(), pairing idx[0][k] with idx[1][k] the way the obs_pair_mass branch does. Worth checking that the > 2 group case (lepton-jet-top-sum-mass) stays excluded, since a triple's mass is not a floor on any pair.

The sqrt(s_hat) bound in phasespace.cpp reads the same table, so it follows for free.

🤖 Generated with Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions