Exemple #1
0
    def test_class_E(self):
        """
        Testing with LHE event:
         21 -1 0 0 502 501 +0.0000000000e+00 +0.0000000000e+00 +4.1758290104e+02 4.1758290104e+02
         21 -1 0 0 501 502 -0.0000000000e+00 -0.0000000000e+00 -9.3527493892e+00 9.3527493892e+00
         25  2 1 2   0   0 +0.0000000000e+00 -1.7763568394e-15 +4.0823015165e+02 4.2693565043e+02
        -24  2 3 3   0   0 +3.1392005803e+01 +3.9830546942e+00 +2.4197449191e+02 2.5697832303e+02
        -11  1 3 3   0   0 -1.8065699537e+01 +5.1144469720e+00 +7.7093957249e+01 7.9347371172e+01
         13  1 4 4   0   0 +3.6021334597e+01 +3.3915572129e+01 +1.9426779434e+02 2.0046889658e+02
         12  1 3 3   0   0 -1.3326306266e+01 -9.0975016661e+00 +8.9161702490e+01 9.0609956220e+01
        -14  1 4 4   0   0 -4.6293287943e+00 -2.9932517435e+01 +4.7706697567e+01 5.6509426451e+01
        """
        p4_e = FourMomentum(7.9347371172e+01, -1.8065699537e+01, +5.1144469720e+00, +7.7093957249e+01)
        p4_mu = FourMomentum(2.0046889658e+02, +3.6021334597e+01, +3.3915572129e+01, +1.9426779434e+02)
        p4_ve = FourMomentum(9.0609956220e+01, -1.3326306266e+01, -9.0975016661e+00, +8.9161702490e+01)
        p4_vm = FourMomentum(5.6509426451e+01, -4.6293287943e+00, -2.9932517435e+01, +4.7706697567e+01)

        p4_w1 = p4_e + p4_ve
        p4_w2 = p4_mu + p4_vm
        p4_h = p4_w1 + p4_w2

        input_event = Event(13000)
        input_event.add_particle("e-", Particle(-11, 2, p4_e))
        input_event.add_particle("mu+", Particle(13, 2, p4_mu))

        class_E_sols = cv.class_E(input_event, ["nu1", "nu2", "e-", "mu+"], p4_w1.mass(), p4_w2.mass(), p4_h.mass(), 1.89940637)

        passed = False
        for sol in class_E_sols:
            if p4_ve == sol["nu1"].four_momentum and p4_vm == sol["nu2"].four_momentum:
                passed = True
        self.assertTrue(passed)
Exemple #2
0
def block_D(event_list,
            particle_keys,
            sqrt_s12,
            theta_1=None,
            phi_1=None,
            m1=0,
            m2=-1):
    """
    s_12  ╭──  p2  (visible)
    ┄┄┄┄┄┄┤
          ╰── |p1| (missing)
    """
    solutions = []
    if not isinstance(event_list, list):
        if isinstance(event_list, Event):
            event_list = [event_list]
        else:
            raise TypeError("'event_list' should be a list of Event objects")

    for event in event_list:
        p2 = event[particle_keys[1]]
        if particle_keys[0] not in event.particles:
            event.add_particle(particle_keys[0],
                               Particle(0, 1, FourMomentum()))
        p1 = event[particle_keys[0]]

        m2 = set_mass(m2, p2)

        s12 = sqrt_s12**2
        R12 = (s12 - m1**2 - m2**2) / 2.0

        # Make unit vectors to get cos(angle)
        theta_1, phi_1 = p1.theta, p1.phi
        theta_2, phi_2 = p2.theta, p2.phi
        u1 = ThreeVector.from_mag_and_angles(1.0, theta_1, phi_1)
        u2 = ThreeVector.from_mag_and_angles(1.0, theta_2, phi_2)
        cos_angle_difference = u1 * u2

        # Now solve 2nd order polynomial
        denominator = 1 / (p2.three_vector().mag2() * cos_angle_difference**2)
        E1_sols = np.roots(
            np.nan_to_num([
                1 - p2.E**2 * denominator, 2 * p2.E * R12 * denominator,
                -m1**2 - R12**2 * denominator
            ]))
        E1_sols = remove_complex(E1_sols)

        for E1_sol in E1_sols:
            if not valid_energy(E1_sol):
                continue
            p1_mag = (E1_sol**2 - m1**2)**0.5
            if isinstance(p1_mag, complex):
                continue
            output_event = deepcopy(event)
            output_event[particle_keys[0]].four_momentum = \
                FourMomentum.from_mag_and_angles(E1_sol, p1_mag, theta_1, phi_1)
            solutions.append(output_event)

    return solutions
Exemple #3
0
 def test_energy_scaling(self):
     test_particle = Particle(0, 0, FourMomentum(20.0, 5.0, 5.0, 5.0))
     prescaled_energy = test_particle.E
     prescaled_mass = test_particle.mass()
     test_particle.scale_energy(2.0)
     scaled_energy = test_particle.E
     scaled_mass = test_particle.mass()
     self.assertEqual(scaled_energy, 2.0*prescaled_energy)
     self.assertAlmostEqual(prescaled_mass, scaled_mass, 5)
Exemple #4
0
    def test_class_B(self):
        """
        Testing with W+y LHE event:
        -1 -1  0  0    0  501 -0.0000000000e+00 +0.0000000000e+00 +7.4827774544e+00 7.4827774544e+00
        2  -1  0  0  501    0 +0.0000000000e+00 -0.0000000000e+00 -4.0157473806e+02 4.0157473806e+02
        24  2  1  2    0    0 +1.4717751207e+01 +1.9702783714e+01 -2.5724660044e+02 2.7001987857e+02
        -13 1  3  3    0    0 +3.2395050227e+01 -5.3817378176e+00 -4.5790709849e+01 5.6348837518e+01
        14  1  3  3    0    0 -1.7677299020e+01 +2.5084521531e+01 -2.1145589059e+02 2.1367104106e+02
        22  1  1  2    0    0 -1.4717751207e+01 -1.9702783714e+01 -1.3684536016e+02 1.3903763694e+02
        """
        p4_w = FourMomentum(2.7001987857e+02, 1.4717751207e+01, 1.9702783714e+01, -2.5724660044e+02)
        p4_l = FourMomentum(5.6348837518e+01, 3.2395050227e+01, -5.3817378176e+00, -4.5790709849e+01)
        p4_nu = FourMomentum(2.1367104106e+02, -1.7677299020e+01, +2.5084521531e+01, -2.1145589059e+02)
        p4_y = FourMomentum(1.3903763694e+02, -1.4717751207e+01, -1.9702783714e+01, -1.3684536016e+02)
        x1 = 7.4827774544e+00/6500.0
        x2 = 4.0157473806e+02/6500.0

        input_event = Event(13000, l=Particle(-13, 2, p4_l), y=Particle(22, 2, p4_y))

        class_B_sols = cv.class_B(input_event, ["nu", "l"], p4_w.mass())

        passed = False
        for sol in class_B_sols:
            if sol["nu"].four_momentum == p4_nu and abs(sol.x1 - x1) < 1E-5 and abs(sol.x2 - x2) < 1E-5:
                passed = True

        self.assertTrue(passed)
Exemple #5
0
 def test_permutations(self):
     test_event = Event(13000)
     test_event.x1 = 0.5
     test_event.x2 = 0.1
     test_event.add_particle("j/1", Particle(2, 2, FourMomentum(10.0, 1.0, 1.0, 1.0)))
     test_event.add_particle("j/2", Particle(-3, 2, FourMomentum(20.0, 2.0, 2.0, 2.0)))
     test_event.add_particle("v1", Particle(0, 0, FourMomentum()))
     test_event.add_particle("v2", Particle(0, 0, FourMomentum()))
     test_event.add_particle("l/1", Particle(-11, 2, FourMomentum(100.0, 10.0, 10.0, 10.0)))
     test_event.add_particle("l/2", Particle(11, 2, FourMomentum(200.0, 20.0, 20.0, 20.0)))
     test_event.add_particle("l/3", Particle(13, 2, FourMomentum(300.0, 30.0, 30.0, 30.0)))
     test_event.add_particle("b/1", Particle(5, 2, FourMomentum(1000.0, 100.0, 100.0, 100.0)))
     permutations = test_event.make_permutations()
     # Should add some extra code to check the permutations, but for now the inspection seems okay
     self.assertEqual(len(permutations), 2*1*3*2*1*1)
Exemple #6
0
    def test_block_D(self):
        """
        Testing with a Higgs -> bb event
         25  2  1  2   0   0 -8.26690105298e+01 -9.73227425370e+00 +4.43133829811e+01  1.56579999860e+02
         5   1 13 13 504   0 -1.10535285330e+02 +1.52021136715e+01 +4.01076612446e+01  1.18658619796e+02
        -5   1 13 13   0 504 +2.78662748006e+01 -2.49343879252e+01 +4.20572173656e+00  3.79213800645e+01
        """

        p4_b1 = FourMomentum(1.18658619796e+02, -1.10535285330e+02, +1.52021136715e+01, +4.01076612446e+01)
        p4_b2 = FourMomentum(3.79213800645e+01, +2.78662748006e+01, -2.49343879252e+01, +4.20572173656e+00)
        p4_h = FourMomentum(1.56579999860e+02, -8.26690105298e+01, -9.73227425370e+00, +4.43133829811e+01)

        input_event = Event(13000, pb1=Particle(5, 2, p4_b1), pb2=Particle(5, 2, p4_b2))
        block_D_sols = cv.block_D(input_event, ["pb1", "pb2"], p4_h.mass(), p4_b1.theta, p4_b1.phi, p4_b1.mass())
        
        passed = False
        for sol in block_D_sols:
            if p4_b1 == sol["pb1"].four_momentum:
                passed = True
        self.assertTrue(passed)
Exemple #7
0
    def test_class_D(self):
        """
        Testing with LHE event:
         21 -1  0  0 501 502 +0.00000000000e+00 +0.00000000000e+00 +1.54270123210e+02  1.54270123210e+02
         21 -1  0  0 502 503 +0.00000000000e+00 +0.00000000000e+00 -1.78313397110e+03  1.78313397110e+03
         6   2  1  2 501   0 +2.21796373830e+02 -1.39027387052e+02 -6.28762112083e+02  7.02379511610e+02
         5   1  3  3 501   0 +8.09346302052e+01 -1.07816045715e+02 -3.87091333836e+02  4.09922559527e+02
         24  2  3  3   0   0 +1.40861743625e+02 -3.12113413375e+01 -2.41670778248e+02  2.92456952084e+02
        -13  1  5  5   0   0 +1.19984134623e+02 -4.38501699278e+01 -2.34469954878e+02  2.67011590955e+02
         14  1  5  5   0   0 +2.08776090020e+01 +1.26388285904e+01 -7.20082336998e+00  2.54453611289e+01
        -6   2  1  2   0 503 -1.39127363300e+02 +1.48759661306e+02 -1.04441511879e+03  1.07844458284e+03
        -5   1  8  8   0 503 -1.23288432443e+02 +2.98887004115e+01 -5.95113687202e+02  6.08502886337e+02
        -24  2  8  8   0   0 -1.58389308569e+01 +1.18870960894e+02 -4.49301431586e+02  4.69941696503e+02
         13  1 10 10   0   0 -3.56865149184e+01 +4.85974772445e+01 -2.63406562920e+02  2.70218910387e+02
        -14  1 10 10   0   0 +1.98475840615e+01 +7.02734836498e+01 -1.85894868666e+02  1.99722786115e+02
         25  2  1  2   0   0 -8.26690105298e+01 -9.73227425370e+00 +4.43133829811e+01  1.56579999860e+02
         5   1 13 13 504   0 -1.10535285330e+02 +1.52021136715e+01 +4.01076612446e+01  1.18658619796e+02
        -5   1 13 13   0 504 +2.78662748006e+01 -2.49343879252e+01 +4.20572173656e+00  3.79213800645e+01
        """
        p4_t1 = FourMomentum(7.02379511610e+02, +2.21796373830e+02, -1.39027387052e+02, -6.28762112083e+02)
        p4_t2 = FourMomentum(1.07844458284e+03, -1.39127363300e+02, +1.48759661306e+02, -1.04441511879e+03)
        p4_W1 = FourMomentum(2.92456952084e+02, +1.40861743625e+02, -3.12113413375e+01, -2.41670778248e+02)
        p4_W2 = FourMomentum(4.69941696503e+02, -1.58389308569e+01, +1.18870960894e+02, -4.49301431586e+02)
        p4_b1 = FourMomentum(4.09922559527e+02, +8.09346302052e+01, -1.07816045715e+02, -3.87091333836e+02)
        p4_b2 = FourMomentum(6.08502886337e+02, -1.23288432443e+02, +2.98887004115e+01, -5.95113687202e+02)
        p4_mu1 = FourMomentum(2.67011590955e+02, +1.19984134623e+02, -4.38501699278e+01, -2.34469954878e+02)
        p4_mu2 = FourMomentum(2.70218910387e+02, -3.56865149184e+01, +4.85974772445e+01, -2.63406562920e+02)
        p4_nu1 = FourMomentum(2.54453611289e+01, +2.08776090020e+01, +1.26388285904e+01, -7.20082336998e+00)
        p4_nu2 = FourMomentum(1.99722786115e+02, +1.98475840615e+01, +7.02734836498e+01, -1.85894868666e+02)
        p4_h = FourMomentum(1.56579999860e+02, -8.26690105298e+01, -9.73227425370e+00, +4.43133829811e+01)
        p4_hb1 = FourMomentum(1.18658619796e+02, -1.10535285330e+02, +1.52021136715e+01, +4.01076612446e+01)
        p4_hb2 = FourMomentum(3.79213800645e+01, +2.78662748006e+01, -2.49343879252e+01, +4.20572173656e+00)
        x1 = 1.54270123210e+02/6500.0
        x2 = 1.78313397110e+03/6500.0

        input_event = Event(13000, mu1=Particle(-13, 2, p4_mu1), b1=Particle(5, 2, p4_b1), 
            mu2=Particle(13, 2, p4_mu2), b2=Particle(-5, 2, p4_b2), hb1=Particle(5, 2, p4_hb1), hb2=Particle(-5, 2, p4_hb2))

        class_D_sols = cv.class_D(input_event, ["nu1", "nu2", "mu1", "b1", "mu2", "b2"], p4_t1.mass(), p4_t2.mass(), p4_W1.mass(), p4_W2.mass())
        passed = False
        for sol in class_D_sols:
            if sol["nu1"].four_momentum == p4_nu1 and sol["nu2"].four_momentum == p4_nu2 and abs(sol.x1 - x1) < 1E-5 and abs(sol.x2 - x2) < 1E-5:
                passed = True

        self.assertTrue(passed)
Exemple #8
0
def lhe_event_loop(lhe_filename, ordering, sqrt_s=13000):
    with open(lhe_filename, 'r') as lhe_file:
        reading_event = False
        for line in lhe_file:
            if reading_event:
                lhe_event += line
                ls = line.split()
                if len(ls) == 13:
                    if int(ls[1]) == -1:
                        z_mom = float(ls[8])
                        if z_mom > 0.0:
                            event.x1 = z_mom/(event.sqrt_s/2)
                        else:
                            event.x2 = -z_mom/(event.sqrt_s/2)
                    if int(ls[1]) == 1:
                        if abs(int(ls[0])) == 12 or abs(int(ls[0])) == 14:
                            status = 1
                        else:
                            status = 2
                        event.add_particle(ordering[index], Particle(int(ls[0]), status, FourMomentum(float(ls[9]), float(ls[6]), float(ls[7]), float(ls[8]))))
                        index += 1
            if "<event>" in line:
                reading_event = True
                event = Event(sqrt_s)
                lhe_event = ""
                index = 0
            elif "</event>" in line:
                reading_event = False
                lhe_event = lhe_event.replace("\n</event>", "")
                yield event, lhe_event
Exemple #9
0
def class_B(event_list, particle_keys, sqrt_s12, m1=0, m2=-1):
    """
    Gets the missing momentum for the following topology:
            
     x1 ╲     s_12 ╭── p2  (visible)
         █████┄┄┄┄┄┤          
     x2 ╱          ╰── p1  (missing)    
                   
    m2 can be specified, if -1 it is calculated
    """
    solutions = []
    if not isinstance(event_list, list):
        if isinstance(event_list, Event):
            event_list = [event_list]
        else:
            raise TypeError("'event_list' should be a list of Event objects")

    for event in event_list:
        p2 = event[particle_keys[1]]
        p_branches = FourMomentum(0.0, 0.0, 0.0, 0.0)
        for k in event.particles:
            if event.is_visible(k):
                p_branches += event[k]

        if particle_keys[0] not in event.particles:
            event.add_particle(particle_keys[0],
                               Particle(0, 1, FourMomentum()))

        p1x = -1 * p_branches.x
        p1y = -1 * p_branches.y

        m2 = set_mass(m2, p2)

        s12 = sqrt_s12**2

        R12 = (s12 - m1**2 - m2**2) / 2.0

        # Solve polynomial equation for p1z (Ax^2 + Bx + C)
        A = 1 - p2.z**2 / p2.E**2
        B = -2.0 * p2.z / p2.E**2 * (R12 + p1x * p2.x + p1y * p2.y)
        C = m1**2 + p1x**2 + p1y**2 - 1.0/p2.E**2*(R12**2 + p1x**2*p2.x**2 + p1y**2*p2.y**2 + \
            2*(R12*p1x*p2.x + R12*p1y*p2.y + p1x*p2.x*p1y*p2.y))
        p1z_sols = np.roots([A, B, C])
        E1_sols = (m1**2 + p1x**2 + p1y**2 + p1z_sols**2)**0.5

        E1_sols = remove_complex(E1_sols)

        for E1, p1z in zip(E1_sols, p1z_sols):
            if not valid_energy(E1):
                continue
            p1_sol = FourMomentum(E1, p1x, p1y, p1z)
            if abs(p1_sol.m2 - m1**2) > FP_TOLERANCE:
                continue
            x1 = (E1 + p1z + p_branches.E + p_branches.z) / event.sqrt_s
            x2 = (E1 - p1z + p_branches.E - p_branches.z) / event.sqrt_s
            if x1 < 0.0 or x2 < 0.0 or x1 > 1.0 or x2 > 1.0:
                continue
            output_event = deepcopy(event)
            output_event.set_momentum(particle_keys[0], p1_sol)
            output_event.x1 = x1
            output_event.x2 = x2
            solutions.append(output_event)

    return solutions
Exemple #10
0
def class_E(event_list,
            particle_keys,
            sqrt_s13,
            sqrt_s24,
            sqrt_s_hat,
            rapidity,
            m1=0,
            m2=0,
            m3=-1,
            m4=-1):
    """
    Gets the two missing momenta for the following topology:
                     ╭─────── missing (p1)
    x1 ╲       s_13 ╱╰─────── visible (p3)
        ╲          ╱
         ███┄┄┄┄███
        ╱   s_hat  ╲ 
    x2 ╱       s_24 ╲╭─────── missing (p2)
                     ╰─────── visible (p4)
    x1 and x2 can be set manually for debugging
    m1, ..., m4 can be specified, if -1 they are calculated
    """
    solutions = []
    if not isinstance(event_list, list):
        if isinstance(event_list, Event):
            event_list = [event_list]
        else:
            raise TypeError("'event_list' should be a list of Event objects")

    for event in event_list:
        # Set up the kinematic variables
        s_13, s_24 = sqrt_s13**2, sqrt_s24**2

        p3 = event[particle_keys[2]]
        p4 = event[particle_keys[3]]
        p_branches = FourMomentum(0.0, 0.0, 0.0, 0.0)
        for k in event.particles:
            if event.is_visible(k):
                p_branches += event[k]

        [event.add_particle(key, Particle(0, 1, FourMomentum())) for key in particle_keys[:2] \
            if not key in event.particles]

        x1 = math.exp(rapidity) * sqrt_s_hat / event.sqrt_s
        x2 = math.exp(-rapidity) * sqrt_s_hat / event.sqrt_s

        m3 = set_mass(m3, p3)
        m4 = set_mass(m4, p4)

        p_i1 = FourMomentum(x1 * event.sqrt_s / 2, 0.0, 0.0,
                            x1 * event.sqrt_s / 2)
        p_i2 = FourMomentum(x2 * event.sqrt_s / 2, 0.0, 0.0,
                            -x2 * event.sqrt_s / 2)
        p_vis = p_i1 + p_i2 - p_branches

        Rvis = (m2**2 - m1**2 + p_vis * p_vis) / 2
        R3 = p_vis * p3 - (s_13 - m1**2 - m3**2) / 2
        R4 = (s_24 - m2**2 - m4**2) / 2

        # Solved equations in terms of E2 from SymPy
        # p = E2*v_s + v_i <- vector equation
        PF = 1/(p3.x*p4.y*p_vis.z - p3.x*p4.z*p_vis.y - p3.y*p4.x*p_vis.z + \
            p3.y*p4.z*p_vis.x + p3.z*p4.x*p_vis.y - p3.z*p4.y*p_vis.x)
        v_i = PF*np.array([-(R3*p4.y*p_vis.z - R3*p4.z*p_vis.y - R4*p3.y*p_vis.z + \
                        R4*p3.z*p_vis.y + Rvis*p3.y*p4.z - Rvis*p3.z*p4.y),
                        (R3*p4.x*p_vis.z - R3*p4.z*p_vis.x - R4*p3.x*p_vis.z + \
                        R4*p3.z*p_vis.x + Rvis*p3.x*p4.z - Rvis*p3.z*p4.x),
                        -(R3*p4.x*p_vis.y - R3*p4.y*p_vis.x - R4*p3.x*p_vis.y + \
                        R4*p3.y*p_vis.x + Rvis*p3.x*p4.y - Rvis*p3.y*p4.x)],
                    dtype=np.float)
        v_s = PF*np.array([-(-p3.E*p4.y*p_vis.z + p3.E*p4.z*p_vis.y + p3.y*p4.E*p_vis.z - \
                        p3.y*p4.z*p_vis.E - p3.z*p4.E*p_vis.y + p3.z*p4.y*p_vis.E),
                        (-p3.E*p4.x*p_vis.z + p3.E*p4.z*p_vis.x + p3.x*p4.E*p_vis.z - \
                        p3.x*p4.z*p_vis.E - p3.z*p4.E*p_vis.x + p3.z*p4.x*p_vis.E),
                        -(-p3.E*p4.x*p_vis.y + p3.E*p4.y*p_vis.x + p3.x*p4.E*p_vis.y - \
                        p3.x*p4.y*p_vis.E - p3.y*p4.E*p_vis.x + p3.y*p4.x*p_vis.E)],
                    dtype=np.float)

        # Now the solutions can be taken from E2^2 = m2^2 + p2·p2
        # Using the symbols above, we have A*E2^2 + B*E2 + C = 0
        A = 1 - np.dot(v_s, v_s)
        B = -2 * (np.dot(v_s, v_i))
        C = -(m2**2 + np.dot(v_i, v_i))

        E2_sols = np.roots(np.nan_to_num([A, B, C]))
        E2_sols = remove_complex(E2_sols)

        p2_sols = [
            FourMomentum(E2_sol, *(E2_sol * v_s + v_i)) for E2_sol in E2_sols
        ]
        p1_sols = [p_vis - p2_sol for p2_sol in p2_sols]

        solutions = []
        for p1_sol, p2_sol in zip(p1_sols, p2_sols):
            if valid_energy(p1_sol.E) and valid_energy(p2_sol.E):
                output_event = deepcopy(event)
                output_event.set_momentum(particle_keys[0], p1_sol)
                output_event.set_momentum(particle_keys[1], p2_sol)
                output_event.x1 = x1
                output_event.x2 = x2
                solutions.append(output_event)

    return solutions
Exemple #11
0
def class_D(event_list,
            particle_keys,
            sqrt_s134,
            sqrt_s256,
            sqrt_s13,
            sqrt_s25,
            m1=0,
            m2=0,
            m3=-1,
            m4=-1,
            m5=-1,
            m6=-1):
    """
    Gets the two missing momenta for the following topology:
                   ╭────────── missing (p1)
     x1 ╲    s_13 ╱╰────────── visible (p3)
         ╲       ╱──────────── visible (p4)
          ╲     ╱ s_134
           █████
          ╱     ╲ s_256
         ╱       ╲──────────── visible (p6)
     x2 ╱    s_25 ╲╭────────── visible (p5)
                   ╰────────── missing (p2)
    m3, ..., m6 can be specified, if -1 they are calculated
    """
    solutions = []
    if not isinstance(event_list, list):
        if isinstance(event_list, Event):
            event_list = [event_list]
        else:
            raise TypeError("'event_list' should be a list of Event objects")

    for event in event_list:
        p3 = event[particle_keys[2]]
        p4 = event[particle_keys[3]]
        p5 = event[particle_keys[4]]
        p6 = event[particle_keys[5]]
        p_branches = FourMomentum(0.0, 0.0, 0.0, 0.0)
        for k in event.particles:
            if event.is_visible(k):
                p_branches += event[k]

        [event.add_particle(key, Particle(0, 1, FourMomentum())) for key in particle_keys[:2] \
            if not key in event.particles]

        m3 = set_mass(m3, p3)
        m4 = set_mass(m4, p4)
        m5 = set_mass(m5, p5)
        m6 = set_mass(m6, p6)

        s13 = sqrt_s13**2
        s25 = sqrt_s25**2
        s134 = sqrt_s134**2
        s256 = sqrt_s256**2

        R13 = (s13 - m3**2 - m1**2) / 2.0
        R25 = (s25 - m5**2 - m2**2) / 2.0
        R134 = (s134 - m1**2 - m3**2 - m4**2 - 2 * (p3 * p4)) / 2.0
        R256 = (s256 - m2**2 - m5**2 - m6**2 - 2 * (p5 * p6)) / 2.0
        R14 = R134 - R13
        R26 = R256 - R25

        # First define coefficients for vector equations:
        # p1 = a1 + b1*E1 + c1*E2 / p2 = a2 + b2*E2 + c2*E1
        # Numbers from SymPy
        PF = 1/(p3.x*p4.z*p5.y*p6.z - p3.x*p4.z*p5.z*p6.y - \
            p3.y*p4.z*p5.x*p6.z + p3.y*p4.z*p5.z*p6.x - \
            p3.z*p4.x*p5.y*p6.z + p3.z*p4.x*p5.z*p6.y + \
            p3.z*p4.y*p5.x*p6.z - p3.z*p4.y*p5.z*p6.x)
        a1 = PF*np.array(
            [-(R13*p4.z*p5.y*p6.z - R13*p4.z*p5.z*p6.y - R14*p3.z*p5.y*p6.z + \
            R14*p3.z*p5.z*p6.y + R25*p3.y*p4.z*p6.z - R25*p3.z*p4.y*p6.z - \
            R26*p3.y*p4.z*p5.z + R26*p3.z*p4.y*p5.z - p3.y*p4.z*p5.x*p6.z*p_branches.x - \
            p3.y*p4.z*p5.y*p6.z*p_branches.y + p3.y*p4.z*p5.z*p6.x*p_branches.x + \
            p3.y*p4.z*p5.z*p6.y*p_branches.y + p3.z*p4.y*p5.x*p6.z*p_branches.x + \
            p3.z*p4.y*p5.y*p6.z*p_branches.y - p3.z*p4.y*p5.z*p6.x*p_branches.x - \
            p3.z*p4.y*p5.z*p6.y*p_branches.y),
            (R13*p4.z*p5.x*p6.z - R13*p4.z*p5.z*p6.x - R14*p3.z*p5.x*p6.z + \
            R14*p3.z*p5.z*p6.x + R25*p3.x*p4.z*p6.z - R25*p3.z*p4.x*p6.z - \
            R26*p3.x*p4.z*p5.z + R26*p3.z*p4.x*p5.z - p3.x*p4.z*p5.x*p6.z*p_branches.x - \
            p3.x*p4.z*p5.y*p6.z*p_branches.y + p3.x*p4.z*p5.z*p6.x*p_branches.x + \
            p3.x*p4.z*p5.z*p6.y*p_branches.y + p3.z*p4.x*p5.x*p6.z*p_branches.x + \
            p3.z*p4.x*p5.y*p6.z*p_branches.y - p3.z*p4.x*p5.z*p6.x*p_branches.x - \
            p3.z*p4.x*p5.z*p6.y*p_branches.y),
            (R13*p4.x*p5.y*p6.z - R13*p4.x*p5.z*p6.y - R13*p4.y*p5.x*p6.z + R13*p4.y*p5.z*p6.x - \
            R14*p3.x*p5.y*p6.z + R14*p3.x*p5.z*p6.y + R14*p3.y*p5.x*p6.z - R14*p3.y*p5.z*p6.x - \
            R25*p3.x*p4.y*p6.z + R25*p3.y*p4.x*p6.z + R26*p3.x*p4.y*p5.z - R26*p3.y*p4.x*p5.z + \
            p3.x*p4.y*p5.x*p6.z*p_branches.x + p3.x*p4.y*p5.y*p6.z*p_branches.y - \
            p3.x*p4.y*p5.z*p6.x*p_branches.x - p3.x*p4.y*p5.z*p6.y*p_branches.y - \
            p3.y*p4.x*p5.x*p6.z*p_branches.x - p3.y*p4.x*p5.y*p6.z*p_branches.y + \
            p3.y*p4.x*p5.z*p6.x*p_branches.x + p3.y*p4.x*p5.z*p6.y*p_branches.y)],
            dtype=np.float
        )
        b1 = PF*np.array(
            [-(-p3.E*p4.z*p5.y*p6.z + p3.E*p4.z*p5.z*p6.y + p3.z*p4.E*p5.y*p6.z - p3.z*p4.E*p5.z*p6.y),
            -p3.E*p4.z*p5.x*p6.z + p3.E*p4.z*p5.z*p6.x + p3.z*p4.E*p5.x*p6.z - p3.z*p4.E*p5.z*p6.x,
            (-p3.E*p4.x*p5.y*p6.z + p3.E*p4.x*p5.z*p6.y + p3.E*p4.y*p5.x*p6.z - p3.E*p4.y*p5.z*p6.x + \
            p3.x*p4.E*p5.y*p6.z - p3.x*p4.E*p5.z*p6.y - p3.y*p4.E*p5.x*p6.z + p3.y*p4.E*p5.z*p6.x)],
            dtype=np.float
        )
        c1 = PF * np.array([
            -(-p3.y * p4.z * p5.E * p6.z + p3.y * p4.z * p5.z * p6.E +
              p3.z * p4.y * p5.E * p6.z - p3.z * p4.y * p5.z * p6.E),
            -p3.x * p4.z * p5.E * p6.z + p3.x * p4.z * p5.z * p6.E +
            p3.z * p4.x * p5.E * p6.z - p3.z * p4.x * p5.z * p6.E,
            (p3.x * p4.y * p5.E * p6.z - p3.x * p4.y * p5.z * p6.E -
             p3.y * p4.x * p5.E * p6.z + p3.y * p4.x * p5.z * p6.E)
        ],
                           dtype=np.float)
        a2 = PF*np.array(
            [R13*p4.z*p5.y*p6.z - R13*p4.z*p5.z*p6.y - R14*p3.z*p5.y*p6.z + R14*p3.z*p5.z*p6.y + \
            R25*p3.y*p4.z*p6.z - R25*p3.z*p4.y*p6.z - R26*p3.y*p4.z*p5.z + R26*p3.z*p4.y*p5.z - \
            p3.x*p4.z*p5.y*p6.z*p_branches.x + p3.x*p4.z*p5.z*p6.y*p_branches.x - \
            p3.y*p4.z*p5.y*p6.z*p_branches.y + p3.y*p4.z*p5.z*p6.y*p_branches.y + \
            p3.z*p4.x*p5.y*p6.z*p_branches.x - p3.z*p4.x*p5.z*p6.y*p_branches.x + \
            p3.z*p4.y*p5.y*p6.z*p_branches.y - p3.z*p4.y*p5.z*p6.y*p_branches.y,
            -(R13*p4.z*p5.x*p6.z - R13*p4.z*p5.z*p6.x - R14*p3.z*p5.x*p6.z + R14*p3.z*p5.z*p6.x + \
            R25*p3.x*p4.z*p6.z - R25*p3.z*p4.x*p6.z - R26*p3.x*p4.z*p5.z + R26*p3.z*p4.x*p5.z - \
            p3.x*p4.z*p5.x*p6.z*p_branches.x + p3.x*p4.z*p5.z*p6.x*p_branches.x - \
            p3.y*p4.z*p5.x*p6.z*p_branches.y + p3.y*p4.z*p5.z*p6.x*p_branches.y + \
            p3.z*p4.x*p5.x*p6.z*p_branches.x - p3.z*p4.x*p5.z*p6.x*p_branches.x + \
            p3.z*p4.y*p5.x*p6.z*p_branches.y - p3.z*p4.y*p5.z*p6.x*p_branches.y),
            R13*p4.z*p5.x*p6.y - R13*p4.z*p5.y*p6.x - R14*p3.z*p5.x*p6.y + R14*p3.z*p5.y*p6.x + \
            R25*p3.x*p4.z*p6.y - R25*p3.y*p4.z*p6.x - R25*p3.z*p4.x*p6.y + R25*p3.z*p4.y*p6.x - \
            R26*p3.x*p4.z*p5.y + R26*p3.y*p4.z*p5.x + R26*p3.z*p4.x*p5.y - R26*p3.z*p4.y*p5.x - \
            p3.x*p4.z*p5.x*p6.y*p_branches.x + p3.x*p4.z*p5.y*p6.x*p_branches.x - \
            p3.y*p4.z*p5.x*p6.y*p_branches.y + p3.y*p4.z*p5.y*p6.x*p_branches.y + \
            p3.z*p4.x*p5.x*p6.y*p_branches.x - p3.z*p4.x*p5.y*p6.x*p_branches.x + \
            p3.z*p4.y*p5.x*p6.y*p_branches.y - p3.z*p4.y*p5.y*p6.x*p_branches.y],
            dtype=np.float
        )
        b2 = PF * np.array([
            -p3.E * p4.z * p5.y * p6.z + p3.E * p4.z * p5.z * p6.y +
            p3.z * p4.E * p5.y * p6.z - p3.z * p4.E * p5.z * p6.y,
            -(-p3.E * p4.z * p5.x * p6.z + p3.E * p4.z * p5.z * p6.x +
              p3.z * p4.E * p5.x * p6.z - p3.z * p4.E * p5.z * p6.x),
            -p3.E * p4.z * p5.x * p6.y + p3.E * p4.z * p5.y * p6.x +
            p3.z * p4.E * p5.x * p6.y - p3.z * p4.E * p5.y * p6.x
        ],
                           dtype=np.float)
        c2 = PF*np.array(
            [-p3.y*p4.z*p5.E*p6.z + p3.y*p4.z*p5.z*p6.E + p3.z*p4.y*p5.E*p6.z - p3.z*p4.y*p5.z*p6.E,
            -(-p3.x*p4.z*p5.E*p6.z + p3.x*p4.z*p5.z*p6.E + p3.z*p4.x*p5.E*p6.z - p3.z*p4.x*p5.z*p6.E),
            -p3.x*p4.z*p5.E*p6.y + p3.x*p4.z*p5.y*p6.E + p3.y*p4.z*p5.E*p6.x - p3.y*p4.z*p5.x*p6.E + \
            p3.z*p4.x*p5.E*p6.y - p3.z*p4.x*p5.y*p6.E - p3.z*p4.y*p5.E*p6.x + p3.z*p4.y*p5.x*p6.E],
            dtype=np.float
        )

        # Enforcing the mass-shell equation for p1:
        # E1^2 = m1^2 + p1·p1
        # 0 = A*E1^2 + (B0 + B1*E2)*E1 + C0 + C1*E2 + C2*E2^2
        A = np.dot(b1, b1) - 1
        B0 = 2 * np.dot(a1, b1)
        B1 = 2 * np.dot(b1, c1)
        C0 = np.dot(a1, a1) + m1**2
        C1 = 2 * np.dot(a1, c1)
        C2 = np.dot(c1, c1)
        # 0 = D*E^2 + (F0 + F1*E1)*E2 + G0 + G1*E1 + G2*E1^2
        D = np.dot(c2, c2) - 1
        F0 = 2 * np.dot(a2, c2)
        F1 = 2 * np.dot(b2, c2)
        G0 = np.dot(a2, a2) + m2**2
        G1 = 2 * np.dot(a2, b2)
        G2 = np.dot(b2, b2)

        E2_sols = np.roots(np.nan_to_num([(-A**2*D**2 + A*(B1*D*F1 + 2*C2*D*G2 - C2*F1**2) + \
            G2*(-B1**2*D + B1*C2*F1 - C2**2*G2))/A**2,
            (-2*A**2*D*F0 + A*(B0*D*F1 + B1*D*G1 + B1*F0*F1 + 2*C1*D*G2 - C1*F1**2 + \
            2*C2*F0*G2 - 2*C2*F1*G1) + G2*(-2*B0*B1*D + B0*C2*F1 - B1**2*F0 + B1*C1*F1 + \
            B1*C2*G1 - 2*C1*C2*G2))/A**2,
            (-A**2*(2*D*G0 + F0**2) + A*(B0*D*G1 + B0*F0*F1 + B1*F0*G1 + B1*F1*G0 + \
            2*C0*D*G2 - C0*F1**2 + 2*C1*F0*G2 - 2*C1*F1*G1 + 2*C2*G0*G2 - C2*G1**2) + \
            G2*(-B0**2*D - 2*B0*B1*F0 + B0*C1*F1 + B0*C2*G1 - B1**2*G0 + B1*C0*F1 + B1*C1*G1 - \
            2*C0*C2*G2 - C1**2*G2))/A**2,
            (-2*A**2*F0*G0 + A*(B0*F0*G1 + B0*F1*G0 + B1*G0*G1 + 2*C0*F0*G2 - 2*C0*F1*G1 + \
            2*C1*G0*G2 - C1*G1**2) + G2*(-B0**2*F0 - 2*B0*B1*G0 + B0*C0*F1 + B0*C1*G1 + B1*C0*G1 - 2*C0*C1*G2))/A**2,
            (-A**2*G0**2 + A*(B0*G0*G1 + 2*C0*G0*G2 - C0*G1**2) + G2*(-B0**2*G0 + B0*C0*G1 - C0**2*G2))/A**2]))
        E2_sols = remove_complex(E2_sols)

        # For each E2, there will be 2 p1's, one will have the wrong mass
        for E2_sol in E2_sols:
            if not valid_energy(E2_sol):
                continue
            for pm in [-1, 1]:
                E1_sol = (-B0 - B1 * E2_sol + pm * (
                    ((B0 + B1 * E2_sol)**2 - 4 * A *
                     (C0 + C1 * E2_sol + C2 * E2_sol**2))**0.5)) / (2 * A)
                p1_sol = FourMomentum(E1_sol,
                                      *(a1 + E1_sol * b1 + E2_sol * c1))
                if abs(p1_sol.m2 -
                       m1**2) > FP_TOLERANCE or not valid_energy(E1_sol):
                    continue
                p2_sol = FourMomentum(E2_sol,
                                      *(a2 + E1_sol * b2 + E2_sol * c2))
                if abs(p2_sol.m2 - m2**2) > FP_TOLERANCE:
                    continue
                x1 = (p1_sol.E + p1_sol.z + p_branches.E + p_branches.z +
                      p2_sol.E + p2_sol.z) / event.sqrt_s
                x2 = (p1_sol.E - p1_sol.z + p_branches.E - p_branches.z +
                      p2_sol.E - p2_sol.z) / event.sqrt_s
                if x1 < 0.0 or x2 < 0.0 or x1 > 1.0 or x2 > 1.0:
                    continue
                output_event = deepcopy(event)
                output_event.set_momentum(particle_keys[0], p1_sol)
                output_event.set_momentum(particle_keys[1], p2_sol)
                output_event.x1 = x1
                output_event.x2 = x2
                solutions.append(output_event)

    return solutions