@@ -273,101 +273,123 @@ struct lambdaspincorrderived {
273273 auto proton1LambdaRF = boostLambda1ToCM (proton1pairCM);
274274 auto proton2LambdaRF = boostLambda2ToCM (proton2pairCM);
275275
276- TVector3 zhat (lambda1CM.Px (), lambda1CM.Py (), lambda1CM.Pz ());
277- if (zhat.Mag2 () == 0 )
278- zhat = TVector3 (0 , 0 , 1 );
279- zhat = zhat.Unit ();
280-
281- // pick a reference not collinear with ẑ (beam z works)
282- TVector3 ref (0 ., 0 ., 1 .);
283- if (std::abs (zhat.Dot (ref)) > 0.999 )
284- ref = TVector3 (1 ., 0 ., 0 .);
285-
286- // build transverse axes
287- TVector3 xhat = (ref - (ref.Dot (zhat)) * zhat).Unit ();
288- TVector3 yhat = (zhat.Cross (xhat)).Unit ();
289-
290- // proton unit vectors in their Λ rest frames
291- TVector3 n1 (proton1LambdaRF.Px (), proton1LambdaRF.Py (), proton1LambdaRF.Pz ());
292- n1 = n1.Unit ();
293- TVector3 n2 (proton2LambdaRF.Px (), proton2LambdaRF.Py (), proton2LambdaRF.Pz ());
294- n2 = n2.Unit ();
295-
296- // cosθ* (Λ2 measured along −ẑ)
297- double c1 = n1.Dot (zhat);
298- double c2 = -n2.Dot (zhat);
299-
300- // sinθ*
301- double s1 = std::sqrt (std::max (0.0 , 1.0 - c1 * c1));
302- double s2 = std::sqrt (std::max (0.0 , 1.0 - c2 * c2));
303-
304- // φ1*, φ2* in common convention (flip axes for Λ2)
305- double phi1 = std::atan2 (n1.Dot (yhat), n1.Dot (xhat));
306- double phi2 = std::atan2 (n2.Dot (-yhat), n2.Dot (-xhat));
307-
308- // helicity spin-correlation
309- double cosDelta = c1 * c2 + s1 * s2 * std::cos (phi1 - phi2);
310- // clamp for safety
311- if (cosDelta > 1.0 )
312- cosDelta = 1.0 ;
313- if (cosDelta < -1.0 )
314- cosDelta = -1.0 ;
315-
316- //
317- // --- (B) Beam-based correlation (common z_B = beam; axes transported into each Λ rest frame) ---
318- //
319- TVector3 zB (0 ., 0 ., 1 .); // beam axis in pair–CM coordinates
320-
321- // x_B: projection of Λ1 direction onto plane ⟂ z_B; y_B completes RH basis
276+ // =================== Opening-angle correlator: cos(Δθ) for helicity-z and beam-z ===================
277+
278+ // Proton unit directions in Λ rest frames
279+ TVector3 k1 (proton1LambdaRF.Px (), proton1LambdaRF.Py (), proton1LambdaRF.Pz ());
280+ k1 = k1.Unit ();
281+ TVector3 k2 (proton2LambdaRF.Px (), proton2LambdaRF.Py (), proton2LambdaRF.Pz ());
282+ k2 = k2.Unit ();
283+
284+ // Helper: boost a spacelike axis (t=0) from PRF into a Λ rest frame
285+ auto transport = [](const TVector3& v, const ROOT::Math::Boost& B) -> TVector3 {
286+ ROOT::Math::PxPyPzEVector a (v.X (), v.Y (), v.Z (), 0.0 );
287+ auto ar = B (a);
288+ TVector3 out (ar.Px (), ar.Py (), ar.Pz ());
289+ return (out.Mag2 () > 0 ) ? out.Unit () : out;
290+ };
291+
292+ // ----------------------------- (1) Helicity-z construction -----------------------------
293+ // z along Λ1 in PRF
294+ TVector3 zPRF (lambda1CM.Px (), lambda1CM.Py (), lambda1CM.Pz ());
295+ if (zPRF.Mag2 () == 0 )
296+ zPRF = TVector3 (0 , 0 , 1 );
297+ zPRF = zPRF.Unit ();
298+
299+ // transverse axes in PRF
300+ TVector3 ref (0 , 0 , 1 );
301+ if (std::abs (zPRF.Dot (ref)) > 0.999 )
302+ ref = TVector3 (1 , 0 , 0 );
303+ TVector3 xPRF = (ref - (ref.Dot (zPRF)) * zPRF).Unit ();
304+ TVector3 yPRF = (zPRF.Cross (xPRF)).Unit ();
305+
306+ // carry PRF triad to Λ rest frames (flip triad for Λ2 to keep same PRF-handedness)
307+ TVector3 z1_h = transport (zPRF, boostLambda1ToCM);
308+ TVector3 x1_h = transport (xPRF, boostLambda1ToCM);
309+ TVector3 y1_h = transport (yPRF, boostLambda1ToCM);
310+
311+ TVector3 z2_h = transport (-zPRF, boostLambda2ToCM);
312+ TVector3 x2_h = transport (-xPRF, boostLambda2ToCM);
313+ TVector3 y2_h = transport (-yPRF, boostLambda2ToCM);
314+
315+ // angles and cosΔθ (helicity)
316+ double c1_h = k1.Dot (z1_h);
317+ double s1_h = std::sqrt (std::max (0.0 , 1.0 - c1_h * c1_h));
318+ double phi1_h = std::atan2 (k1.Dot (y1_h), k1.Dot (x1_h));
319+
320+ double c2_h = k2.Dot (z2_h);
321+ double s2_h = std::sqrt (std::max (0.0 , 1.0 - c2_h * c2_h));
322+ double phi2_h = std::atan2 (k2.Dot (y2_h), k2.Dot (x2_h));
323+
324+ double cosDeltaTheta_hel = c1_h * c2_h + s1_h * s2_h * std::cos (phi1_h - phi2_h);
325+ if (cosDeltaTheta_hel > 1.0 )
326+ cosDeltaTheta_hel = 1.0 ;
327+ if (cosDeltaTheta_hel < -1.0 )
328+ cosDeltaTheta_hel = -1.0 ;
329+
330+ // ------------------------------- (2) Beam-z construction -------------------------------
331+ // z along beam in PRF; choose x by projecting Λ1 onto the ⟂ plane to fix azimuth zero
332+ TVector3 zB (0 , 0 , 1 );
322333 TVector3 L1dir (lambda1CM.Px (), lambda1CM.Py (), lambda1CM.Pz ());
323334 L1dir = L1dir.Unit ();
324335 TVector3 xB = L1dir - (L1dir.Dot (zB)) * zB;
325336 if (xB.Mag2 () < 1e-12 )
326- xB = TVector3 (1 . , 0 . , 0 .); // fallback if Λ1 ∥ beam
337+ xB = TVector3 (1 , 0 , 0 );
327338 xB = xB.Unit ();
328339 TVector3 yB = (zB.Cross (xB)).Unit ();
329340
330- // helper to transport an axis into a Λ rest frame (from pair–CM)
331- auto transport = [](const TVector3& v, const ROOT::Math::Boost& B) -> TVector3 {
332- ROOT::Math::PxPyPzEVector a (v.X (), v.Y (), v.Z (), 0.0 ); // spacelike 4-vector (t=0)
333- ROOT::Math::PxPyPzEVector ar = B (a); // boost into that Λ rest frame
334- TVector3 out (ar.Px (), ar.Py (), ar.Pz ());
335- if (out.Mag2 () == 0 )
336- return out;
337- return out.Unit ();
338- };
341+ // carry beam triad to Λ rest frames (no flip for a common external axis)
342+ TVector3 z1_b = transport (zB, boostLambda1ToCM);
343+ TVector3 x1_b = transport (xB, boostLambda1ToCM);
344+ TVector3 y1_b = transport (yB, boostLambda1ToCM);
345+
346+ TVector3 z2_b = transport (zB, boostLambda2ToCM);
347+ TVector3 x2_b = transport (xB, boostLambda2ToCM);
348+ TVector3 y2_b = transport (yB, boostLambda2ToCM);
349+
350+ // angles and cosΔθ (beam)
351+ double c1_b = k1.Dot (z1_b);
352+ double s1_b = std::sqrt (std::max (0.0 , 1.0 - c1_b * c1_b));
353+ double phi1_b = std::atan2 (k1.Dot (y1_b), k1.Dot (x1_b));
354+
355+ double c2_b = k2.Dot (z2_b);
356+ double s2_b = std::sqrt (std::max (0.0 , 1.0 - c2_b * c2_b));
357+ double phi2_b = std::atan2 (k2.Dot (y2_b), k2.Dot (x2_b));
358+
359+ double cosDeltaTheta_beam = c1_b * c2_b + s1_b * s2_b * std::cos (phi1_b - phi2_b);
360+ if (cosDeltaTheta_beam > 1.0 )
361+ cosDeltaTheta_beam = 1.0 ;
362+ if (cosDeltaTheta_beam < -1.0 )
363+ cosDeltaTheta_beam = -1.0 ;
364+
365+ // --- STAR-style Δθ (as written: dot product of proton directions in their own Λ RFs) ---
366+
367+ // Boost each proton into its parent's rest frame
368+ ROOT::Math::Boost boostL1_LabToRF{particle1Dummy.BoostToCM ()}; // Λ1 velocity in lab
369+ ROOT::Math::Boost boostL2_LabToRF{particle2Dummy.BoostToCM ()}; // Λ2 velocity in lab
370+
371+ auto p1_LRF = boostL1_LabToRF (daughpart1);
372+ auto p2_LRF = boostL2_LabToRF (daughpart2);
373+
374+ // Unit 3-vectors (in different rest frames!)
375+ TVector3 u1 = TVector3 (p1_LRF.Px (), p1_LRF.Py (), p1_LRF.Pz ()).Unit ();
376+ TVector3 u2 = TVector3 (p2_LRF.Px (), p2_LRF.Py (), p2_LRF.Pz ()).Unit ();
339377
340- // transport beam triad to each Λ rest frame
341- TVector3 zB1 = transport (zB, boostLambda1ToCM);
342- TVector3 xB1 = transport (xB, boostLambda1ToCM);
343- TVector3 yB1 = transport (yB, boostLambda1ToCM);
344-
345- TVector3 zB2 = transport (zB, boostLambda2ToCM);
346- TVector3 xB2 = transport (xB, boostLambda2ToCM);
347- TVector3 yB2 = transport (yB, boostLambda2ToCM);
348-
349- // angles w.r.t. beam triad (same z_B convention for both Λ’s; no flips)
350- double c1B = n1.Dot (zB1), c2B = n2.Dot (zB2);
351- double s1B = std::sqrt (std::max (0.0 , 1.0 - c1B * c1B));
352- double s2B = std::sqrt (std::max (0.0 , 1.0 - c2B * c2B));
353- double phi1B = std::atan2 (n1.Dot (yB1), n1.Dot (xB1));
354- double phi2B = std::atan2 (n2.Dot (yB2), n2.Dot (xB2));
355-
356- // beam correlation
357- double cosDeltaBeam = c1B * c2B + s1B * s2B * std::cos (phi1B - phi2B);
358- if (cosDeltaBeam > 1.0 )
359- cosDeltaBeam = 1.0 ;
360- if (cosDeltaBeam < -1.0 )
361- cosDeltaBeam = -1.0 ;
378+ // STAR-style cosΔθ definition
379+ double cosDeltaTheta_STAR_naive = u1.Dot (u2);
380+ if (cosDeltaTheta_STAR_naive > 1.0 )
381+ cosDeltaTheta_STAR_naive = 1.0 ;
382+ if (cosDeltaTheta_STAR_naive < -1.0 )
383+ cosDeltaTheta_STAR_naive = -1.0 ;
362384
363385 auto cosThetaDiff = -999.0 ;
364386 auto costhetaz1costhetaz2 = -999.0 ;
365387 if (cosDef == 0 ) {
366- cosThetaDiff = proton1LambdaRF. Vect (). Unit (). Dot (proton2LambdaRF. Vect (). Unit ()) ;
388+ cosThetaDiff = cosDeltaTheta_STAR_naive ;
367389 costhetaz1costhetaz2 = (proton1LambdaRF.Pz () * proton2LambdaRF.Pz ()) / (proton1LambdaRF.P () * proton2LambdaRF.P ());
368390 } else {
369- cosThetaDiff = cosDelta ;
370- costhetaz1costhetaz2 = cosDeltaBeam ;
391+ cosThetaDiff = cosDeltaTheta_hel ;
392+ costhetaz1costhetaz2 = cosDeltaTheta_beam ;
371393 }
372394
373395 double deltaPhi = std::abs (RecoDecay::constrainAngle (particle1Dummy.Phi (), 0 .0F , harmonic) - RecoDecay::constrainAngle (particle2Dummy.Phi (), 0 .0F , harmonic));
0 commit comments