Skip to content

Commit 1a80efc

Browse files
committed
[PWGCF] Add spherical harmonics decomposition to unified Femto framework
1 parent 9a9b82c commit 1a80efc

2 files changed

Lines changed: 436 additions & 0 deletions

File tree

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
// Copyright 2019-2025 CERN and copyright holders of ALICE O2.
2+
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3+
// All rights not expressly granted are reserved.
4+
//
5+
// This software is distributed under the terms of the GNU General Public
6+
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7+
//
8+
// In applying this license CERN does not waive the privileges and immunities
9+
// granted to it by virtue of its status as an Intergovernmental Organization
10+
// or submit itself to any jurisdiction.
11+
12+
/// \file femtoSpherHarMath.h
13+
/// \brief Container for the calculation of spherical harmonics components
14+
/// \author Pritam Chakraborty, WUT Warsaw, pritam.chakraborty@pw.edu.pl
15+
16+
#ifndef PWGCF_FEMTO_CORE_FEMTOSPHERHARMATH_H_
17+
#define PWGCF_FEMTO_CORE_FEMTOSPHERHARMATH_H_
18+
19+
#include <CommonConstants/MathConstants.h>
20+
21+
#include <array>
22+
#include <cmath>
23+
#include <complex>
24+
25+
namespace o2::analysis::femto
26+
{
27+
28+
/// \class SpherHarMath
29+
/// \brief Container for math calculations of quantities related to pairs
30+
class SpherHarMath
31+
{
32+
public:
33+
static constexpr int MaxSupportedL = 5; // analytic Ylm implemented up to l=5
34+
static constexpr int TrigCacheSize = MaxSupportedL + 1; // sin/cos powers cache size (indices 0..5)
35+
static constexpr double SmallLength = 1e-10; // numerical guard for |r|, |z|
36+
37+
/// Values of various coefficients
38+
void initializeYlms()
39+
{
40+
double oneoversqrtpi = 1.0 / std::sqrt(o2::constants::math::PI);
41+
42+
// l=0 prefactors
43+
fgPrefactors[0] = 0.5 * oneoversqrtpi;
44+
45+
// l=1 prefactors
46+
fgPrefactors[1] = 0.5 * std::sqrt(3.0 / 2.0) * oneoversqrtpi;
47+
fgPrefactors[2] = 0.5 * std::sqrt(3.0) * oneoversqrtpi;
48+
fgPrefactors[3] = -fgPrefactors[1];
49+
50+
// l=2 prefactors
51+
fgPrefactors[4] = 0.25 * std::sqrt(15.0 / 2.0) * oneoversqrtpi;
52+
fgPrefactors[5] = 0.5 * std::sqrt(15.0 / 2.0) * oneoversqrtpi;
53+
fgPrefactors[6] = 0.25 * std::sqrt(5.0) * oneoversqrtpi;
54+
fgPrefactors[7] = -fgPrefactors[5];
55+
fgPrefactors[8] = fgPrefactors[4];
56+
57+
// l=3 prefactors
58+
fgPrefactors[9] = 0.125 * std::sqrt(35.0) * oneoversqrtpi;
59+
fgPrefactors[10] = 0.25 * std::sqrt(105.0 / 2.0) * oneoversqrtpi;
60+
fgPrefactors[11] = 0.125 * std::sqrt(21.0) * oneoversqrtpi;
61+
fgPrefactors[12] = 0.25 * std::sqrt(7.0) * oneoversqrtpi;
62+
fgPrefactors[13] = -fgPrefactors[11];
63+
fgPrefactors[14] = fgPrefactors[10];
64+
fgPrefactors[15] = -fgPrefactors[9];
65+
66+
// l=4 prefactors
67+
fgPrefactors[16] = 3.0 / 16.0 * std::sqrt(35.0 / 2.0) * oneoversqrtpi;
68+
fgPrefactors[17] = 3.0 / 8.0 * std::sqrt(35.0) * oneoversqrtpi;
69+
fgPrefactors[18] = 3.0 / 8.0 * std::sqrt(5.0 / 2.0) * oneoversqrtpi;
70+
fgPrefactors[19] = 3.0 / 8.0 * std::sqrt(5.0) * oneoversqrtpi;
71+
fgPrefactors[20] = 3.0 / 16.0 * oneoversqrtpi;
72+
fgPrefactors[21] = -fgPrefactors[19];
73+
fgPrefactors[22] = fgPrefactors[18];
74+
fgPrefactors[23] = -fgPrefactors[17];
75+
fgPrefactors[24] = fgPrefactors[16];
76+
77+
// l=5 prefactors
78+
fgPrefactors[25] = 3.0 / 32.0 * std::sqrt(77.0) * oneoversqrtpi;
79+
fgPrefactors[26] = 3.0 / 16.0 * std::sqrt(385.0 / 2.0) * oneoversqrtpi;
80+
fgPrefactors[27] = 1.0 / 32.0 * std::sqrt(385.0) * oneoversqrtpi;
81+
fgPrefactors[28] = 1.0 / 8.0 * std::sqrt(1155.0 / 2.0) * oneoversqrtpi;
82+
fgPrefactors[29] = 1.0 / 16.0 * std::sqrt(165.0 / 2.0) * oneoversqrtpi;
83+
fgPrefactors[30] = 1.0 / 16.0 * std::sqrt(11.0) * oneoversqrtpi;
84+
fgPrefactors[31] = -fgPrefactors[29];
85+
fgPrefactors[32] = fgPrefactors[28];
86+
fgPrefactors[33] = -fgPrefactors[27];
87+
fgPrefactors[34] = fgPrefactors[26];
88+
fgPrefactors[35] = -fgPrefactors[25];
89+
90+
fgPrefshift[0] = 0;
91+
fgPrefshift[1] = 2;
92+
fgPrefshift[2] = 6;
93+
fgPrefshift[3] = 12;
94+
fgPrefshift[4] = 20;
95+
fgPrefshift[5] = 30;
96+
97+
fgPlmshift[0] = 0;
98+
fgPlmshift[1] = 2;
99+
fgPlmshift[2] = 5;
100+
fgPlmshift[3] = 9;
101+
fgPlmshift[4] = 14;
102+
fgPlmshift[5] = 20;
103+
}
104+
105+
/// Function to calculate Legendre Polynomials
106+
/// \param lmax Maximum value of L component
107+
/// \param ctheta Value of theta
108+
/// \param lbuf values of coefficients
109+
void legendreUpToYlm(int lmax, double ctheta, std::array<double, 36>& lbuf)
110+
{
111+
// Calculate a set of legendre polynomials up to a given l
112+
// with spherical input
113+
std::array<double, 6> sins{};
114+
std::array<double, 6> coss{};
115+
sins[0] = 0.0;
116+
coss[0] = 1.0;
117+
sins[1] = std::sqrt(1 - ctheta * ctheta);
118+
coss[1] = ctheta;
119+
for (int iter = 2; iter < TrigCacheSize; iter++) {
120+
sins[iter] = sins[iter - 1] * sins[1];
121+
coss[iter] = coss[iter - 1] * coss[1];
122+
}
123+
124+
// Legendre polynomials l=0
125+
lbuf[0] = 1.0;
126+
127+
// Legendre polynomials l=1
128+
if (lmax > 0) {
129+
lbuf[1] = sins[1];
130+
lbuf[2] = coss[1];
131+
}
132+
133+
// Legendre polynomials l=2
134+
if (lmax > 1) {
135+
lbuf[3] = sins[2];
136+
lbuf[4] = sins[1] * coss[1];
137+
lbuf[5] = 3 * coss[2] - 1;
138+
}
139+
140+
// Legendre polynomials l=3
141+
if (lmax > 2) { // o2-linter: disable=magic-number (l index, mirrors lmax>0/1 above)
142+
lbuf[6] = sins[3];
143+
lbuf[7] = sins[2] * coss[1];
144+
lbuf[8] = (5 * coss[2] - 1) * sins[1];
145+
lbuf[9] = 5 * coss[3] - 3 * coss[1];
146+
}
147+
148+
// Legendre polynomials l=4
149+
if (lmax > 3) { // o2-linter: disable=magic-number (l index)
150+
lbuf[10] = sins[4];
151+
lbuf[11] = sins[3] * coss[1];
152+
lbuf[12] = (7 * coss[2] - 1) * sins[2];
153+
lbuf[13] = (7 * coss[3] - 3 * coss[1]) * sins[1];
154+
lbuf[14] = 35 * coss[4] - 30 * coss[2] + 3;
155+
}
156+
157+
// Legendre polynomials l=5
158+
if (lmax > 4) { // o2-linter: disable=magic-number (l index)
159+
lbuf[15] = sins[5];
160+
lbuf[16] = sins[4] * coss[1];
161+
lbuf[17] = (9 * coss[2] - 1) * sins[3];
162+
lbuf[18] = (3 * coss[3] - 1 * coss[1]) * sins[2];
163+
lbuf[19] = (21 * coss[4] - 14 * coss[2] + 1) * sins[1];
164+
lbuf[20] = 63 * coss[5] - 70 * coss[3] + 15 * coss[1];
165+
}
166+
}
167+
168+
/// Function to calculate a set of Ylms up to a given l with cartesian input
169+
void doYlmUpToL(int lmax, double x, double y, double z, std::complex<double>* ylms)
170+
{
171+
double ctheta = 0.0;
172+
double phi = 0.0;
173+
174+
double r = std::sqrt(x * x + y * y + z * z);
175+
if (r < SmallLength || std::fabs(z) < SmallLength) {
176+
ctheta = 0.0;
177+
} else {
178+
ctheta = z / r;
179+
}
180+
phi = std::atan2(y, x);
181+
doYlmUpToL(lmax, ctheta, phi, ylms);
182+
}
183+
184+
/// Function to calculate a set of Ylms up to a given l with spherical input
185+
void doYlmUpToL(int lmax, double ctheta, double phi, std::complex<double>* ylms)
186+
{
187+
int lcur = 0;
188+
double lpol = 0.0;
189+
190+
std::array<double, 6> coss{};
191+
std::array<double, 6> sins{};
192+
193+
std::array<double, 36> lbuf{};
194+
legendreUpToYlm(lmax, ctheta, lbuf);
195+
initializeYlms();
196+
197+
for (int iter = 1; iter <= lmax; iter++) {
198+
coss[iter - 1] = std::cos(iter * phi);
199+
sins[iter - 1] = std::sin(iter * phi);
200+
}
201+
202+
ylms[lcur++] = fgPrefactors[0] * lbuf[0] * std::complex<double>(1, 0);
203+
204+
for (int il = 1; il <= lmax; il++) {
205+
// First im = 0
206+
ylms[lcur + il] = fgPrefactors[fgPrefshift[il]] * lbuf[static_cast<int>(fgPlmshift[il])] * std::complex<double>(1.0, 0.0);
207+
// Im != 0
208+
for (int im = 1; im <= il; im++) {
209+
lpol = lbuf[static_cast<int>(fgPlmshift[il]) - im];
210+
ylms[lcur + il - im] = fgPrefactors[fgPrefshift[il] - im] * lpol * std::complex<double>(coss[im - 1], -sins[im - 1]);
211+
ylms[lcur + il + im] = fgPrefactors[fgPrefshift[il] + im] * lpol * std::complex<double>(coss[im - 1], sins[im - 1]);
212+
}
213+
lcur += 2 * il + 1;
214+
}
215+
}
216+
217+
private:
218+
std::array<float, 36> fgPrefactors{};
219+
std::array<int, 10> fgPrefshift{};
220+
std::array<int, 10> fgPlmshift{};
221+
};
222+
223+
} // namespace o2::analysis::femto
224+
225+
#endif // PWGCF_FEMTO_CORE_FEMTOSPHERHARMATH_H_

0 commit comments

Comments
 (0)