Skip to content

Commit 3e90446

Browse files
authored
[PWGLF] Add new definition of spin correlation same as STAR to compare (#13136)
1 parent 6d8d857 commit 3e90446

File tree

1 file changed

+103
-81
lines changed

1 file changed

+103
-81
lines changed

PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx

Lines changed: 103 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)