Skip to content

Commit a25cf4a

Browse files
authored
Add three flavour barger propagator (#139)
* to be used in testing for comparison with tensor based propagator
1 parent cfde3fe commit a25cf4a

File tree

2 files changed

+281
-0
lines changed

2 files changed

+281
-0
lines changed

python/binding.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,4 +419,39 @@ void initTesting(py::module &m)
419419
py::arg("energy"), py::arg("i"), py::arg("j")
420420
)
421421
;
422+
423+
py::class_<testing::ThreeFlavourBarger>(m_testing, "ThreeFlavourBarger")
424+
.def(py::init<>())
425+
.def("set_params", &testing::ThreeFlavourBarger::setParams,
426+
py::arg("m1"), py::arg("m2"), py::arg("m3"), py::arg("theta12"), py::arg("theta13"), py::arg("theta23"), py::arg("deltaCP"), py::arg("baseline"), py::arg("density") = (float)-999.9, py::arg("anti_neutrino") = false
427+
)
428+
.def("alpha", &testing::ThreeFlavourBarger::alpha,
429+
"Calculates alpha term used in calculating the mass eigenvalues",
430+
py::arg("energy")
431+
)
432+
.def("beta", &testing::ThreeFlavourBarger::beta,
433+
"Calculates beta term used in calculating the mass eigenvalues",
434+
py::arg("energy")
435+
)
436+
.def("gamma", &testing::ThreeFlavourBarger::gamma,
437+
"Calculates gamma term used in calculating the mass eigenvalues",
438+
py::arg("energy")
439+
)
440+
.def("calculate_effective_m2", &testing::ThreeFlavourBarger::calculateEffectiveM2,
441+
"Calculates the effective hamiltonian eigenvalues (the M^2) in matter",
442+
py::arg("energy"), py::arg("index")
443+
)
444+
.def("get_hamiltonian_element", &testing::ThreeFlavourBarger::getHamiltonianElement,
445+
"Calculates an element of the Hamiltonian",
446+
py::arg("energy"), py::arg("a"), py::arg("b")
447+
)
448+
.def("get_transition_matrix_element", &testing::ThreeFlavourBarger::getTransitionMatrixElement,
449+
"Calculates an element of the transition matrix from one mass eigenstate to another due to the presense of matter",
450+
py::arg("energy"), py::arg("a"), py::arg("b")
451+
)
452+
.def("calculate_prob", &testing::ThreeFlavourBarger::calculateProb,
453+
"Calculate probability of transitioning from state i to state j for a given energy",
454+
py::arg("energy"), py::arg("i"), py::arg("j")
455+
)
456+
;
422457
}

tests/barger-propagator.hpp

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
#pragma once
22

3+
#include <array>
4+
35
#include <cmath>
6+
#include <complex>
47

58
#include <iostream>
69
#include <nuTens/propagator/constants.hpp>
@@ -195,6 +198,249 @@ class TwoFlavourBarger
195198
bool _antiNeutrino;
196199
};
197200

201+
class ThreeFlavourBarger
202+
{
203+
public:
204+
205+
// set the parameters of this propagator
206+
// negative density values will be interpreted as propagating in vacuum
207+
inline void setParams(
208+
double m1, double m2, double m3,
209+
double theta12, double theta13, double theta23,
210+
double deltaCP,
211+
double baseline, double density = -999.9f, bool antiNeutrino = false)
212+
{
213+
_m1 = m1;
214+
_m2 = m2;
215+
_m3 = m3;
216+
_theta12 = theta12;
217+
_theta13 = theta13;
218+
_theta23 = theta23;
219+
_deltaCP = deltaCP;
220+
_baseline = baseline;
221+
_density = density;
222+
_antiNeutrino = antiNeutrino;
223+
224+
// fill the mass array
225+
masses[0] = _m1;
226+
masses[1] = _m2;
227+
masses[2] = _m3;
228+
229+
// fill the PMNS matrix elements
230+
pmnsMatrix[0][0] = std::complex<double>(std::cos(theta12) * std::cos(theta13), 0.0);
231+
pmnsMatrix[0][1] = std::complex<double>(std::sin(theta12) * std::cos(theta13), 0.0);
232+
pmnsMatrix[0][2] = std::sin(theta13) * std::exp(std::complex<double>(0.0, -1.0) * deltaCP);
233+
234+
pmnsMatrix[1][0] = -std::sin(theta12) * std::cos(theta23) - std::cos(theta12) * std::sin(theta23) * std::sin(theta13) * std::exp(std::complex<double>(0.0, 1.0) * deltaCP);
235+
pmnsMatrix[1][1] = std::cos(theta12) * std::cos(theta23) - std::sin(theta12) * std::sin(theta23) * std::sin(theta13) * std::exp(std::complex<double>(0.0, 1.0) * deltaCP);
236+
pmnsMatrix[1][2] = std::complex<double>(std::sin(theta23) * std::cos(theta13), 0.0);
237+
238+
pmnsMatrix[2][0] = std::sin(theta12) * std::sin(theta23) - std::cos(theta12) * std::cos(theta23) * std::sin(theta13) * std::exp(std::complex<double>(0.0, 1.0) * deltaCP);
239+
pmnsMatrix[2][1] = -std::cos(theta12) * std::sin(theta23) - std::sin(theta12) * std::cos(theta23) * std::sin(theta13) * std::exp(std::complex<double>(0.0, 1.0) * deltaCP);
240+
pmnsMatrix[2][2] = std::complex<double>(std::cos(theta23) * std::cos(theta13), 0.0);
241+
242+
};
243+
244+
/// calculate the alpha factor used in the eigenvalue computation
245+
[[nodiscard]] inline double alpha(float energy) const
246+
{
247+
float dmsq12 = _m1 * _m1 - _m2 * _m2;
248+
float dmsq13 = _m1 * _m1 - _m3 * _m3;
249+
250+
double ret;
251+
252+
if (_antiNeutrino) {
253+
ret = - 2.0 * constants::Groot2 * energy * _density + dmsq12 + dmsq13;
254+
}
255+
else {
256+
ret = 2.0 * constants::Groot2 * energy * _density + dmsq12 + dmsq13;
257+
}
258+
259+
return ret;
260+
}
261+
262+
/// calculate the beta factor used in the eigenvalue computation
263+
[[nodiscard]] inline double beta(float energy) const
264+
{
265+
double dmsq12 = _m1 * _m1 - _m2 * _m2;
266+
double dmsq13 = _m1 * _m1 - _m3 * _m3;
267+
268+
double ret;
269+
270+
if (_antiNeutrino) {
271+
ret = (
272+
dmsq12 * dmsq13 +
273+
- 2.0 * constants::Groot2 * energy * _density * (
274+
dmsq12 * (1.0 - std::abs(pmnsMatrix[0][1]) * std::abs(pmnsMatrix[0][1])) +
275+
dmsq13 * (1.0 - std::abs(pmnsMatrix[0][2]) * std::abs(pmnsMatrix[0][2]))
276+
)
277+
);
278+
}
279+
else {
280+
ret = (
281+
dmsq12 * dmsq13 +
282+
2.0 * constants::Groot2 * energy * _density * (
283+
dmsq12 * (1.0 - std::abs(pmnsMatrix[0][1]) * std::abs(pmnsMatrix[0][1])) +
284+
dmsq13 * (1.0 - std::abs(pmnsMatrix[0][2]) * std::abs(pmnsMatrix[0][2]))
285+
)
286+
);
287+
}
288+
289+
return ret;
290+
}
291+
292+
/// calculate the gamma factor used in the eigenvalue computation
293+
[[nodiscard]] inline double gamma(float energy) const
294+
{
295+
float dmsq12 = _m1 * _m1 - _m2 * _m2;
296+
float dmsq13 = _m1 * _m1 - _m3 * _m3;
297+
298+
double ret;
299+
if (_antiNeutrino) {
300+
ret = -2 * constants::Groot2 * energy * _density * dmsq12 * dmsq13 * std::abs(pmnsMatrix[0][0]) * std::abs(pmnsMatrix[0][0]);
301+
}
302+
else {
303+
ret = 2 * constants::Groot2 * energy * _density * dmsq12 * dmsq13 * std::abs(pmnsMatrix[0][0]) * std::abs(pmnsMatrix[0][0]);
304+
}
305+
306+
return ret;
307+
}
308+
309+
/// calculate effective M^2 values (eigenvalues of the hamiltonian) due to matter effects
310+
/// @param energy The neutrino energy
311+
/// @param index The index of the eigenvalue. should be [0-2]
312+
[[nodiscard]] inline double calculateEffectiveM2(float energy, int index) const
313+
{
314+
float a = alpha(energy);
315+
float b = beta(energy);
316+
float c = gamma(energy);
317+
318+
// calculate argument of arccos
319+
float arg = (2.0 * a*a*a - 9.0 * a*b + 27.0 * c) / ( 2.0 * std::pow( a*a - 3.0 * b, 3.0 / 2.0) );
320+
321+
// calculate the coefficient of the cos term
322+
float coeff = - (2.0 / 3.0) * std::sqrt( a*a - 3.0 * b );
323+
324+
return coeff * std::cos( ( 1.0 / 3.0 ) * ( std::acos(arg) + index * 2.0 * M_PI ) ) + _m1 * _m1 - a / 3.0;
325+
}
326+
327+
/// @brief Calculate an element of the hamiltonian
328+
/// @param energy The neutrino energy
329+
/// @param k Row
330+
/// @param j Column
331+
/// @return Matrix element
332+
[[nodiscard]] inline std::complex<double> getHamiltonianElement(float energy, int a, int b) const {
333+
334+
std::complex<double> ret = 0.0;
335+
336+
if ( a == b ) {
337+
ret += masses[a] * masses[a] / (2.0 * energy);
338+
}
339+
340+
if (_antiNeutrino) {
341+
ret += constants::Groot2 * _density * std::conj(pmnsMatrix[0][b]) * pmnsMatrix[0][a];
342+
}
343+
else {
344+
ret -= constants::Groot2 * _density * pmnsMatrix[0][b] * std::conj(pmnsMatrix[0][a]);
345+
}
346+
347+
return ret;
348+
}
349+
350+
/// @brief Calculate an element of the "X" transition matrix matrix (equation 11 in Barger et al)
351+
/// @param energy The neutrino energy
352+
/// @param a Row
353+
/// @param b Column
354+
/// @return Matrix element
355+
[[nodiscard]] inline std::complex<double> getTransitionMatrixElement(float energy, int a, int b) const {
356+
357+
std::complex<double> ret = 0.0;
358+
359+
for (int k = 0; k < 3; k++) {
360+
361+
std::complex<double> numerator = 4.0 * energy * energy * (
362+
getHamiltonianElement(energy, a, 0) * getHamiltonianElement(energy, 0, b) +
363+
getHamiltonianElement(energy, a, 1) * getHamiltonianElement(energy, 1, b) +
364+
getHamiltonianElement(energy, a, 2) * getHamiltonianElement(energy, 2, b)
365+
);
366+
367+
std::complex<double> constant = 1.0;
368+
std::complex<double> denominator = 1.0;
369+
370+
for (int j = 0; j < 3; j++) {
371+
372+
if (j == k) continue;
373+
374+
numerator -= 2.0 * energy * getHamiltonianElement(energy, a, b) * calculateEffectiveM2(energy, j);
375+
denominator *= calculateEffectiveM2(energy, k) - calculateEffectiveM2(energy, j);
376+
constant *= calculateEffectiveM2(energy, j);
377+
378+
}
379+
380+
std::complex<double> prod = numerator;
381+
382+
if (a == b)
383+
prod += constant;
384+
385+
prod /= denominator;
386+
387+
std::complex<double> exponential = std::exp(-std::complex<double>(0.0, 1.0) * calculateEffectiveM2(energy, k) * _baseline * 2.0 * M_PI / (2.0 * energy));
388+
389+
ret += prod * exponential;
390+
}
391+
392+
return ret;
393+
}
394+
395+
/// @brief calculate oscillation probability from flavour alpha to flavour beta
396+
/// @param energy neutrino energy
397+
/// @param alpha initial flavour index
398+
/// @param beta final flavour index
399+
/// @return oscillation probability
400+
[[nodiscard]] inline double calculateProb(float energy, int alpha, int beta) const {
401+
402+
std::complex<double> ret = 0.0;
403+
404+
for (int i = 0; i < 3; i++) {
405+
406+
for (int j = 0; j < 3; j++) {
407+
408+
if (_antiNeutrino) {
409+
ret += std::conj(pmnsMatrix[beta][i] * getTransitionMatrixElement(energy, i, j)) * pmnsMatrix[alpha][j];
410+
}
411+
else {
412+
ret += pmnsMatrix[alpha][i] * getTransitionMatrixElement(energy, i, j) * std::conj(pmnsMatrix[beta][j]);
413+
}
414+
415+
}
416+
417+
}
418+
419+
return std::abs(ret) * std::abs(ret);
420+
}
421+
422+
423+
private:
424+
// oscillation parameters
425+
double _m1;
426+
double _m2;
427+
double _m3;
428+
double _theta12;
429+
double _theta13;
430+
double _theta23;
431+
double _deltaCP;
432+
433+
// other parameters
434+
double _baseline;
435+
double _density;
436+
437+
// anti-neutrino flag
438+
bool _antiNeutrino;
439+
440+
std::array<std::array<std::complex<double>, 3>, 3> pmnsMatrix;
441+
std::array<double, 3> masses;
442+
};
443+
198444
} // namespace testing
199445

200446
} // namespace nuTens

0 commit comments

Comments
 (0)