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 sum_row_kernel(mix, bla, inp, out):
    C = cuda.local.array(shape=(3, 3), dtype=ftype)
    D = cuda.local.array(shape=(3), dtype=ctype)
    E = cuda.local.array(shape=(3), dtype=ctype)
    matrix_dot_matrix(mix, mix, C)
    D[0] = 0.0 + 2.0j
    D[1] = 1.0 + 2.0j
    D[2] = 1.0 + 2.0j
    matrix_dot_vector(C, D, E)
    bla *= 0.1
    out[0] += E[1].real * bla.real + inp[0]
Beispiel #4
0
def get_transition_matrix(
    nubar,
    energy,
    rho,
    baseline,
    mix_nubar,
    mix_nubar_conj_transp,
    mat_pot,
    H_vac,
    dm,
    transition_matrix,
):
    """ Calculate neutrino flavour transition amplitude matrix

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

    energy : real float
        Neutrino energy, GeV

    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)

    baseline : real float
        Baseline, km

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

    mix_nubar_conj_transp : complex conjugate 2d array
        Conjugate transpose of mix_nubar

    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

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

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

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

    Notes
    -----
    For neutrino (nubar > 0) or antineutrino (nubar < 0) with energy energy
    traversing layer of matter of uniform density rho with thickness baseline

    """

    H_mat = cuda.local.array(shape=(3, 3), dtype=ctype)
    dm_mat_vac = cuda.local.array(shape=(3, 3), dtype=ctype)
    dm_mat_mat = cuda.local.array(shape=(3, 3), dtype=ctype)
    H_full = cuda.local.array(shape=(3, 3), dtype=ctype)
    tmp = cuda.local.array(shape=(3, 3), dtype=ctype)
    H_mat_mass_eigenstate_basis = cuda.local.array(shape=(3, 3), dtype=ctype)

    # Compute the matter potential including possible generalized interactions
    # in the flavor basis
    get_H_mat(rho, mat_pot, nubar, H_mat)

    # Get the full Hamiltonian by adding together matter and vacuum parts
    one_over_two_e = 0.5 / energy
    for i in range(3):
        for j in range(3):
            H_full[i, j] = H_vac[i, j] * one_over_two_e + H_mat[i, j]

    # Calculate modified mass eigenvalues in matter from the full Hamiltonian and
    # the vacuum mass splittings
    get_dms(energy, H_full, dm, dm_mat_mat, dm_mat_vac)

    # Now we transform the matter (TODO: matter? full?) Hamiltonian back into the
    # mass eigenstate basis so we don't need to compute products of the effective
    # mixing matrix elements explicitly
    matrix_dot_matrix(H_mat, mix_nubar, tmp)
    matrix_dot_matrix(mix_nubar_conj_transp, tmp, H_mat_mass_eigenstate_basis)

    # We can now proceed to calculating the transition amplitude from the Hamiltonian
    # in the mass basis and the effective mass splittings
    get_transition_matrix_massbasis(
        baseline,
        energy,
        dm_mat_vac,
        dm_mat_mat,
        H_mat_mass_eigenstate_basis,
        transition_matrix,
    )
Beispiel #5
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 #6
0
def get_transition_matrix(nubar, energy, rho, baseline, mix_nubar,
                          mix_nubar_conj_transp, nsi_eps, H_vac, dm,
                          transition_matrix):
    ''' Calculate neutrino flavour transition amplitude matrix

    Parameters
    ----------

    nubar : int

    energy : float

    baseline : float

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

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

    nsi_eps : 2d-array

    H_vac : 2d-array

    dm : 2d-array

    transition_matrix : 2d-array (empty)
        in mass eigenstate basis

    Notes
    -----
    for neutrino (nubar > 0) or antineutrino (nubar < 0)
    with energy energy traversing layer of matter of
    uniform density rho with thickness baseline

    '''

    H_mat = cuda.local.array(shape=(3, 3), dtype=ctype)
    dm_mat_vac = cuda.local.array(shape=(3, 3), dtype=ctype)
    dm_mat_mat = cuda.local.array(shape=(3, 3), dtype=ctype)
    H_full = cuda.local.array(shape=(3, 3), dtype=ctype)
    tmp = cuda.local.array(shape=(3, 3), dtype=ctype)
    H_mat_mass_eigenstate_basis = cuda.local.array(shape=(3, 3), dtype=ctype)

    # Compute the matter potential including possible non-standard interactions
    # in the flavor basis
    get_H_mat(rho, nsi_eps, nubar, H_mat)

    # Get the full Hamiltonian by adding together matter and vacuum parts
    one_over_two_e = 0.5 / energy
    for i in range(3):
        for j in range(3):
            H_full[i, j] = H_vac[i, j] * one_over_two_e + H_mat[i, j]

    # Calculate modified mass eigenvalues in matter from the full Hamiltonian and
    # the vacuum mass splittings
    get_dms(energy, H_full, dm, dm_mat_mat, dm_mat_vac)

    # Now we transform the matter (TODO: matter? full?) Hamiltonian back into the
    # mass eigenstate basis so we don't need to compute products of the effective
    # mixing matrix elements explicitly
    matrix_dot_matrix(H_mat, mix_nubar, tmp)
    matrix_dot_matrix(mix_nubar_conj_transp, tmp, H_mat_mass_eigenstate_basis)

    # We can now proceed to calculating the transition amplitude from the Hamiltonian
    # in the mass basis and the effective mass splittings
    get_transition_matrix_massbasis(baseline, energy, dm_mat_vac, dm_mat_mat,
                                    H_mat_mass_eigenstate_basis,
                                    transition_matrix)