Beispiel #1
0
def get_H_vac(mix_nubar, mix_nubar_conj_transp, dm_vac_vac, H_vac):
    """ Calculate vacuum Hamiltonian in flavor basis for neutrino or antineutrino

    Parameters:
    -----------
    mix_nubar : complex 2d array
        Mixing matrix, already conjugated if antineutrino

    mix_nubar_conj_transp : conjugate 2d array
        Conjugate transpose of mix_nubar

    dm_vac_vac : 2d array
        Matrix of mass splittings

    H_vac : complex 2d array
        Hamiltonian in vacuum, without the 1/2E term


    Notes
    ------
    The Hailtonian does not contain the energy dependent factor of
    1/(2 * E), as it will be added later

    """

    dm_vac_diag = cuda.local.array(shape=(3, 3), dtype=ctype)
    tmp = cuda.local.array(shape=(3, 3), dtype=ctype)

    clear_matrix(dm_vac_diag)

    dm_vac_diag[1, 1] = dm_vac_vac[1, 0] + 0j
    dm_vac_diag[2, 2] = dm_vac_vac[2, 0] + 0j

    matrix_dot_matrix(dm_vac_diag, mix_nubar_conj_transp, tmp)
    matrix_dot_matrix(mix_nubar, tmp, H_vac)
Beispiel #2
0
def get_H_vac(mix_nubar, mix_nubar_conj_transp, dm_vac_vac, H_vac):
    ''' Calculate vacuum Hamiltonian in flavor basis for neutrino or antineutrino

    Parameters:
    -----------
    mix_nubar : complex 2d-array
        Mixing matrix (comjugate for anti-neutrinos)

    mix_nubar_conj_transp : comjugate 2d-array
        conjugate transpose of mixing matrix

    dm_vac_vac: 2d-array
        Matrix of mass splittings

    H_vac: complex 2d-array (empty)
        Hamiltonian in vacuum, modulo a factor 2 * energy

    Notes
    ------
    The Hailtonian does not contain the energy dependent factor of
    1/(2 * E), as it will be added later

    '''
    dm_vac_diag = cuda.local.array(shape=(3, 3), dtype=ctype)
    tmp = cuda.local.array(shape=(3, 3), dtype=ctype)

    clear_matrix(dm_vac_diag)

    dm_vac_diag[1, 1] = dm_vac_vac[1, 0] + 0j
    dm_vac_diag[2, 2] = dm_vac_vac[2, 0] + 0j

    matrix_dot_matrix(dm_vac_diag, mix_nubar_conj_transp, tmp)
    matrix_dot_matrix(mix_nubar, tmp, H_vac)
Beispiel #3
0
def get_transition_matrix_massbasis(
    baseline,
    energy,
    dm_mat_vac,
    dm_mat_mat,
    H_mat_mass_eigenstate_basis,
    transition_matrix,
):
    """
    Calculate the transition amplitude matrix

    Parameters
    ----------
    baseline : float
        Baseline traversed, km

    energy : float
        Neutrino energy, GeV

    dm_mat_vac : complex 2d array

    dm_mat_mat : complex 2d array

    H_mat_mass_eigenstate_basis : complex 2d array

    transition_matrix : complex 2d array (empty)
        Transition matrix in mass eigenstate basis

    Notes
    -----
    - corrsponds to matrix A (equation 10) in original Barger paper
    - take into account generic potential matrix (=Hamiltonian)

    """

    product = cuda.local.array(shape=(3, 3, 3), dtype=ctype)

    clear_matrix(transition_matrix)

    get_product(energy, dm_mat_vac, dm_mat_mat, H_mat_mass_eigenstate_basis,
                product)

    # (1/2)*(1/(h_bar*c)) in units of GeV/(eV^2 km)
    hbar_c_factor = 2.534

    for k in range(3):
        arg = -dm_mat_vac[k, 0] * (baseline / energy) * hbar_c_factor
        c = cmath.exp(arg * 1.0j)
        for i in range(3):
            for j in range(3):
                transition_matrix[i, j] += c * product[i, j, k]
Beispiel #4
0
def get_H_mat(rho, mat_pot, nubar, H_mat):
    """ Calculate matter Hamiltonian in flavor basis

    Parameters:
    -----------
    rho : real float
        Electron number density (in moles/cm^3) (numerically, this is just the
        product of electron fraction and mass density in g/cm^3, since the
        number of grams per cm^3 corresponds to the number of moles of nucleons
        per cm^3)

    mat_pot : complex 2d array
        Generalised matter potential matrix without "a" factor (will be
        multiplied with "a" factor); set to diag([1, 0, 0]) for only standard
        oscillations

    nubar : int
        +1 for neutrinos, -1 for antineutrinos

    H_mat : complex 2d array (empty)
        matter hamiltonian

    Notes
    -----
    In the following, `a` is just the standard effective matter potential
    induced by charged-current weak interactions with electrons

    """

    # 2*sqrt(2)*Gfermi in (eV^2 cm^3)/(mole GeV)
    tworttwoGf = 1.52588e-4
    a = 0.5 * rho * tworttwoGf

    # standard matter interaction Hamiltonian
    clear_matrix(H_mat)

    # formalism of Hamiltonian: not 1+epsilon_ee^f in [0,0] element but just epsilon...
    #   changed when fitting in Thomas' NSI branch -EL

    # Obtain effective non-standard matter interaction Hamiltonian
    #   changed when fitting in Thomas' NSI branch -EL
    for i in range(3):
        for j in range(3):
            # matter potential V -> -V* for anti-neutrinos
            if nubar == -1:
                H_mat[i, j] = -a * mat_pot[i, j].conjugate()
            elif nubar == 1:
                H_mat[i, j] = a * mat_pot[i, j]
Beispiel #5
0
def get_H_mat(rho, nsi_eps, nubar, H_mat):
    ''' Calculate matter Hamiltonian in flavor basis

    Parameters:
    -----------
    rho : float
        density

    nsi_eps : complex 2-d array
        Non-standard interaction terms

    nubar : int
        +1 for neutrinos, -1 for antineutrinos

    H_mat : complex 2d-array (empty)

    Notes
    -----
    in the following, `a` is just the standard effective matter potential
    induced by charged-current weak interactions with electrons

    '''

    # 2*sqrt(2)*Gfermi in (eV^2-cm^3)/(mole-GeV)
    tworttwoGf = 1.52588e-4
    a = 0.5 * rho * tworttwoGf
    if nubar == -1:
        a = -a

    # standard matter interaction Hamiltonian
    clear_matrix(H_mat)
    H_mat[0, 0] = a

    # Obtain effective non-standard matter interaction Hamiltonian
    nsi_rho_scale = 3.  #// assume 3x electron density for "NSI"-quark (e.g., d) density
    fact = nsi_rho_scale * a
    for i in range(3):
        for j in range(3):
            H_mat[i, j] += fact * nsi_eps[i, j]
Beispiel #6
0
def osc_probs_layers_kernel(dm, mix, mat_pot, nubar, energy, density_in_layer,
                            distance_in_layer, osc_probs):
    """ Calculate oscillation probabilities

    given layers of length and density

    Parameters
    ----------
    dm : real 2d array
        Mass splitting matrix, eV^2

    mix : complex 2d array
        PMNS mixing matrix

    mat_pot : complex 2d array
        Generalised matter potential matrix without "a" factor (will be
        multiplied with "a" factor); set to diag([1, 0, 0]) for only standard
        oscillations

    nubar : real int, scalar or Nd array (broadcast dim)
        1 for neutrinos, -1 for antineutrinos

    energy : real float, scalar or Nd array (broadcast dim)
        Neutrino energy, GeV

    density_in_layer : real 1d array
        Density of each layer, moles of electrons / cm^2

    distance_in_layer : real 1d array
        Distance of each layer traversed, km

    osc_probs : real (N+2)-d array (empty)
        Returned oscillation probabilities in the form:
        osc_prob[i,j] = probability of flavor i to oscillate into flavor j
        with 0 = electron, 1 = muon, 3 = tau


    Notes
    -----
    !!! Right now, because of CUDA, the maximum number of layers
    is hard coded and set to 120 (59Layer PREM + Atmosphere).
    This is used for cached layer computation, where earth layer, which
    are typically traversed twice (it's symmetric) are not recalculated
    but rather cached..

    """

    # 3x3 complex
    H_vac = cuda.local.array(shape=(3, 3), dtype=ctype)
    mix_nubar = cuda.local.array(shape=(3, 3), dtype=ctype)
    mix_nubar_conj_transp = cuda.local.array(shape=(3, 3), dtype=ctype)
    transition_product = cuda.local.array(shape=(3, 3), dtype=ctype)
    transition_matrix = cuda.local.array(shape=(3, 3), dtype=ctype)
    tmp = cuda.local.array(shape=(3, 3), dtype=ctype)

    clear_matrix(H_vac)
    clear_matrix(osc_probs)

    # 3-vector complex
    raw_input_psi = cuda.local.array(shape=(3), dtype=ctype)
    output_psi = cuda.local.array(shape=(3), dtype=ctype)

    use_mass_eigenstates = False

    cache = True
    # cache = False

    # TODO:
    # * ensure convention below is respected in MC reweighting
    #   (nubar > 0 for nu, < 0 for anti-nu)
    # * nubar is passed in, so could already pass in the correct form
    #   of mixing matrix, i.e., possibly conjugated
    if nubar > 0:
        # in this case the mixing matrix is left untouched
        copy_matrix(mix, mix_nubar)

    else:
        # here we need to complex conjugate all entries
        # (note that this only changes calculations with non-clear_matrix deltacp)
        conjugate(mix, mix_nubar)

    conjugate_transpose(mix_nubar, mix_nubar_conj_transp)

    get_H_vac(mix_nubar, mix_nubar_conj_transp, dm, H_vac)

    if cache:
        # allocate array to store all the transition matrices
        # doesn't work in cuda...needs fixed shape
        transition_matrices = cuda.local.array(shape=(120, 3, 3), dtype=ctype)

        # loop over layers
        for i in range(distance_in_layer.shape[0]):
            density = density_in_layer[i]
            distance = distance_in_layer[i]
            if distance > 0.0:
                layer_matrix_index = -1
                # chaeck if exists
                for j in range(i):
                    # if density_in_layer[j] == density and distance_in_layer[j] == distance:
                    if (abs(density_in_layer[j] - density) < 1e-5) and (
                            abs(distance_in_layer[j] - distance) < 1e-5):
                        layer_matrix_index = j

                # use from cached
                if layer_matrix_index >= 0:
                    for j in range(3):
                        for k in range(3):
                            transition_matrices[i, j, k] = transition_matrices[
                                layer_matrix_index, j, k]

                # only calculate if necessary
                else:
                    get_transition_matrix(
                        nubar,
                        energy,
                        density,
                        distance,
                        mix_nubar,
                        mix_nubar_conj_transp,
                        mat_pot,
                        H_vac,
                        dm,
                        transition_matrix,
                    )
                    # copy
                    for j in range(3):
                        for k in range(3):
                            transition_matrices[i, j, k] = transition_matrix[j,
                                                                             k]
            else:
                # identity matrix
                for j in range(3):
                    for k in range(3):
                        if j == k:
                            transition_matrix[j, k] = 0.0
                        else:
                            transition_matrix[j, k] = 1.0

        # now multiply them all
        first_layer = True
        for i in range(distance_in_layer.shape[0]):
            distance = distance_in_layer[i]
            if distance > 0.0:
                for j in range(3):
                    for k in range(3):
                        transition_matrix[j, k] = transition_matrices[i, j, k]
                if first_layer:
                    copy_matrix(transition_matrix, transition_product)
                    first_layer = False
                else:
                    matrix_dot_matrix(transition_matrix, transition_product,
                                      tmp)
                    copy_matrix(tmp, transition_product)

    else:
        # non-cache loop
        first_layer = True
        for i in range(distance_in_layer.shape[0]):
            density = density_in_layer[i]
            distance = distance_in_layer[i]
            # only do something if distance > 0.
            if distance > 0.0:
                get_transition_matrix(
                    nubar,
                    energy,
                    density,
                    distance,
                    mix_nubar,
                    mix_nubar_conj_transp,
                    mat_pot,
                    H_vac,
                    dm,
                    transition_matrix,
                )
                if first_layer:
                    copy_matrix(transition_matrix, transition_product)
                    first_layer = False
                else:
                    matrix_dot_matrix(transition_matrix, transition_product,
                                      tmp)
                    copy_matrix(tmp, transition_product)

    # convrt to flavour eigenstate basis
    matrix_dot_matrix(transition_product, mix_nubar_conj_transp, tmp)
    matrix_dot_matrix(mix_nubar, tmp, transition_product)

    # loop on neutrino types, and compute probability for neutrino i:
    for i in range(3):
        for j in range(3):
            raw_input_psi[j] = 0.0

        if use_mass_eigenstates:
            convert_from_mass_eigenstate(i + 1, mix_nubar, raw_input_psi)
        else:
            raw_input_psi[i] = 1.0

        matrix_dot_vector(transition_product, raw_input_psi, output_psi)
        osc_probs[i][0] += output_psi[0].real**2 + output_psi[0].imag**2
        osc_probs[i][1] += output_psi[1].real**2 + output_psi[1].imag**2
        osc_probs[i][2] += output_psi[2].real**2 + output_psi[2].imag**2
Beispiel #7
0
def osc_probs_vacuum_kernel(dm, mix, nubar, energy, distance_in_layer,
                            osc_probs):
    ''' Calculate vacumm mixing probabilities

    Parameters
    ----------

    dm : 2d-array
        Mass splitting matrix

    mix : complex 2d-array
        PMNS mixing matrix

    nubar : int
        +1 for neutrinos, -1 for antineutrinos

    energy : float
        Neutrino energy

    distance_in_layer : 1d-array
        Baselines (will be summed up)

    osc_probs : 2d-array (empty)
        Returned oscillation probabilities in the form:
        osc_prob[i,j] = probability of flavor i to oscillate into flavor j
        with 0 = electron, 1 = muon, 3 = tau


    Notes
    -----

    This is largely unvalidated so far

    '''

    # no need to conjugate mix matrix, as we anyway only need real part
    # can this be right?

    clear_matrix(osc_probs)
    osc_probs_local = cuda.local.array(shape=(3, 3), dtype=ftype)

    # sum up length from all layers
    baseline = 0.
    for i in range(distance_in_layer.shape[0]):
        baseline += distance_in_layer[i]

    # make more precise 20081003 rvw
    l_over_e = 1.26693281 * baseline / energy
    s21 = math.sin(dm[1, 0] * l_over_e)
    s32 = math.sin(dm[2, 0] * l_over_e)
    s31 = math.sin((dm[2, 1] + dm[3, 2]) * l_over_e)

    # does anybody understand this loop?
    # ista = abs(*nutype) - 1
    for ista in range(3):
        for iend in range(2):
            osc_probs_local[ista, iend] = (
                (mix[ista, 0].real * mix[ista, 1].real * s21)**2 +
                (mix[ista, 1].real * mix[ista, 2].real * s32)**2 +
                (mix[ista, 2].real * mix[ista, 0].real * s31)**2)
            if iend == ista:
                osc_probs_local[ista,
                                iend] = 1. - 4. * osc_probs_local[ista, iend]
            else:
                osc_probs_local[ista, iend] = -4. * osc_probs_local[ista, iend]

        osc_probs_local[ista,
                        2] = 1. - osc_probs_local[ista,
                                                  0] - osc_probs_local[ista, 1]

    # is this necessary?
    if nubar > 0:
        copy_matrix(osc_probs_local, osc_probs)
    else:
        for i in range(3):
            for j in range(3):
                osc_probs[i, j] = osc_probs_local[j, i]