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