Exemple #1
0
def _get_integrand(x_log, *, model, electron_dist, photon_energy, z, efd=True):
    """
    Return the value of the integrand for the thick- or thin-target bremsstrahlung models.

    Parameters
    ----------
    x_log : `numpy.array`
        Log of the electron energies
    model : `str`
        Either `thick-target` or `thin-target`
    electron_dist : `BrokenPowerLawElectronDistribution`
        Electron distribution as function of energy
    photon_energy : `numpy.array`
        Photon energies
    z : `float`
        Mean atomic number of plasma
    efd: `bool` (optional)
        True (default) the electron flux distribution (electrons cm^-2 s^-1 keV^-1) is calculated
        with `~sunxspex.emission.BrokenPowerLawElectronDistribution.flux`. False, the electron
        density distribution (electrons cm^-3 keV^-1) is calculated with
        `~sunxspex.emission.BrokenPowerLawElectronDistribution.density`.

    Returns
    -------
    `numpy.array`
        The values of the integrand at the given electron_energies

    References
    ----------
    See SSW
    `brm2_fthin.pro <https://hesperia.gsfc.nasa.gov/ssw/packages/xray/idl/brm2/brm2_fthin.pro>`_ and
    `brm2_fouter.pro <https://hesperia.gsfc.nasa.gov/ssw/packages/xray/idl/brm2/brm2_fouter.pro>`_.

    """
    mc2 = const.get_constant('mc2')
    clight = const.get_constant('clight')

    # L=log10 (E), E=l0L and dE=10L ln(10) dL hence the electron_energy * np.log(10) below
    electron_energy = 10**x_log
    brem_cross = bremsstrahlung_cross_section(electron_energy, photon_energy, z)
    collision_loss = collisional_loss(electron_energy)
    pc = np.sqrt(electron_energy * (electron_energy + 2.0 * mc2))

    density = electron_dist.density(electron_energy)
    if model == 'thick-target':
        return (electron_energy * np.log(10) * density * brem_cross * pc
                / collision_loss / ((electron_energy / mc2) + 1.0))
    elif model == 'thin-target':
        if efd:
            return (electron_energy * np.log(10) * electron_dist.flux(electron_energy)
                    * brem_cross*(mc2/clight))
        else:
            return (electron_energy * np.log(10) * electron_dist.flux(electron_energy)
                    * brem_cross * pc/((electron_energy / mc2) + 1.0))
Exemple #2
0
def collisional_loss(electron_energy):
    """
    Compute the energy dependant terms of the collisional energy loss rate for energetic electrons.

    Parameters
    ----------
    electron_energy : `numpy.array`
        Array of electron energies at which to evaluate loss

    Returns
    -------
    `numpy.array`
        Energy loss rate

    Notes
    -----
    Initial version modified from SSW
    `Brm_ELoss <https://hesperia.gsfc.nasa.gov/ssw/packages/xray/idl/brm/brm_eloss.pro>`_
    """
    electron_rest_mass = const.get_constant('mc2')  # * u.keV #c.m_e * c.c**2

    gamma = (electron_energy / electron_rest_mass) + 1.0

    beta = np.sqrt(1.0 - (1.0 / gamma**2))

    # TODO figure out what number is?
    energy_loss_rate = np.log(6.9447e+9 * electron_energy) / beta

    return energy_loss_rate
Exemple #3
0
def bremsstrahlung_thick_target(photon_energies, p, eebrk, q, eelow, eehigh):
    """
    Computes the thick-target bremsstrahlung x-ray/gamma-ray spectrum from an isotropic electron
    distribution function provided in `broken_powerlaw_f`. The units of the computed flux is photons
    per second per keV per square centimeter.

    The electron flux distribution function is a double power law in electron energy with a
    low-energy cutoff and a high-energy cutoff.

    Parameters
    ----------
    photon_energies : `numpy.array`
        Array of photon energies to evaluate flux at
    p : `float`
        Slope below the break energy
    eebrk : `float`
        Break energy
    q : `float`
        Slope above the break energy
    eelow : `float`
        Low energy electron cut off
    eehigh : `float`
        High energy electron cut off

    Returns
    -------
    `numpy.array`
        flux The computed bremsstrahlung photon flux at the given photon energies.
        Array of photon fluxes (in photons s^-1 keV^-1 cm^-2), when multiplied by a0 * 1.0d+35,
        corresponding to the photon energies in the input array eph.
        The detector is assumed to be 1 AU from the source.
        a0 is the total integrated electron flux, in units of 10^35 electrons s^-1.

    Notes
    -----
    If you want to plot the derivative of the flux, or the spectral index of the photon spectrum as
    a function of photon energy, you should set RERR to 1.d-6, because it is more sensitive to RERR
    than the flux.

    Adapted from SSW `Brm2_ThickTarget
    <https://hesperia.gsfc.nasa.gov/ssw/packages/xray/idl/brm2/brm2_thicktarget.pro>`_
    """
    # Constants
    mc2 = const.get_constant('mc2')
    clight = const.get_constant('clight')
    au = const.get_constant('au')
    r0 = const.get_constant('r0')

    # Max number of points
    maxfcn = 2048

    # Average atomic number
    z = 1.2

    # Relative error
    rerr = 1e-4

    # Numerical coefficient for photo flux
    fcoeff = ((clight**2 / mc2**4) / (4 * np.pi * au**2))

    decoeff = 4.0 * np.pi * (r0**2) * clight

    # Create arrays for the photon flux and error flags.
    flux = np.zeros_like(photon_energies, dtype=np.float64)
    iergq = np.zeros_like(photon_energies, dtype=np.float64)

    if eelow >= eehigh:
        return flux

    i, = np.where((photon_energies < eehigh) & (photon_energies > 0))

    if i.size > 0:
        flux[i], iergq[i] = split_and_integrate(
            model='thick-target',
            photon_energies=photon_energies[i],
            maxfcn=maxfcn,
            rerr=rerr,
            eelow=eelow,
            eebrk=eebrk,
            eehigh=eehigh,
            p=p,
            q=q,
            z=z,
            efd=False)

        flux = (fcoeff / decoeff) * flux

        return flux
    else:
        raise Warning(
            'The photon energies are higher than the highest electron energy or not '
            'greater than zero')
Exemple #4
0
def bremsstrahlung_thin_target(photon_energies,
                               p,
                               eebrk,
                               q,
                               eelow,
                               eehigh,
                               efd=True):
    """
    Computes the thin-target bremsstrahlung x-ray/gamma-ray spectrum from an isotropic electron
    distribution function provided in `broken_powerlaw`. The units of the computed flux is photons
    per second per keV per square centimeter.

    The electron flux distribution function is a double power law in electron energy with a
    low-energy cutoff and a high-energy cutoff.

    Parameters
    ----------
    photon_energies : `numpy.array`
        Array of photon energies to evaluate flux at
    p : `float`
        Slope below the break energy
    eebrk : `float`
        Break energy
    q : `float`
        Slope above the break energy
    eelow : `float`
        Low energy electron cut off
    eehigh : `float`
        High energy electron cut off
    efd : `bool`
        True (default) - input electron distribution is electron flux density distribution
        (unit electrons cm^-2 s^-1 keV^-1),
        False - input electron distribution is electron density distribution.
        (unit electrons cm^-3 keV^-1),
        This input is not used in the main routine, but is passed to brm2_dmlin and Brm2_Fthin

    Returns
    -------
    flux: `numpy.array`
        Multiplying the output of Brm2_ThinTarget by a0 gives an array of
        photon fluxes in photons s^-1 keV^-1 cm^-2, corresponding to the photon energies in the
        input array eph. The detector is assumed to be 1 AU rom the source. The coefficient a0 is
        calculated as a0 = nth * V * nnth, where nth: plasma density; cm^-3) V:
        volume of source; cm^3) nnth: Integrated nonthermal electron flux density (cm^-2 s^-1), if
        efd = True, or Integrated electron number density (cm^-3), if efd = False

    Notes
    -----
    If you want to plot the derivative of the flux, or the spectral index of the photon spectrum as
    a function of photon energy, you should set RERR to 1.d-6, because it is more sensitive to RERR
    than the flux.

    Adapted from SSW `Brm2_ThinTarget
    <https://hesperia.gsfc.nasa.gov/ssw/packages/xray/idl/brm2/brm2_thintarget.pro>`_
    """
    mc2 = const.get_constant('mc2')
    clight = const.get_constant('clight')
    au = const.get_constant('au')

    # Max number of points
    maxfcn = 2048

    # Average atomic number
    z = 1.2

    # Relative error
    rerr = 1e-4

    # Numerical coefficient for photo flux
    fcoeff = (clight / (4 * np.pi * au**2)) / mc2**2.

    # Create arrays for the photon flux and error flags.
    flux = np.zeros_like(photon_energies, dtype=np.float64)
    iergq = np.zeros_like(photon_energies, dtype=np.float64)

    if eelow >= eehigh:
        raise ValueError('eehigh must be larger than eelow!')

    l, = np.where((photon_energies < eehigh) & (photon_energies > 0))
    if l.size > 0:
        flux[l], iergq[l] = split_and_integrate(
            model='thin-target',
            photon_energies=photon_energies[l],
            maxfcn=maxfcn,
            rerr=rerr,
            eelow=eelow,
            eebrk=eebrk,
            eehigh=eehigh,
            p=p,
            q=q,
            z=z,
            efd=efd)

        flux *= fcoeff

        return flux
    else:
        raise Warning(
            'The photon energies are higher than the highest electron energy or not '
            'greater than zero')
Exemple #5
0
def split_and_integrate(*, model, photon_energies, maxfcn, rerr, eelow, eebrk,
                        eehigh, p, q, z, efd):
    """
    Split and integrate the continuous parts of the electron spectrum.

    This is used for thin-target calculation from a double power-law electron density distribution
    To integrate a function via the method of Gaussian quadrature. Repeatedly doubles the number of
    points evaluated until convergence, specified by the input rerr, is obtained, or the maximum
    number of points, specified by the input maxfcn, is reached. If integral convergence is not
    achieved, this function raises a ValueError when either the maximum number of function
    evaluations is performed or the number of Gaussian points to be evaluated exceeds maxfcn.
    Maxfcn should be less than or equal to 2^nlim, or 4096 with nlim = 12. This function splits the
    numerical integration into up to three parts and returns the sum of the parts. This avoids
    numerical problems with discontinuities in the electron distribution function at eelow and
    eebrk.

    Parameters
    ----------
    model : `str`
        Electron model either `thick-target` or `thin-target`
    photon_energies : `numpy.array`
        Array containing lower integration limits
    maxfcn : `int`
        Maximum number of points used in Gaussian quadrature integration
    rerr : `float`
        Desired relative error for integral evaluation
    eelow : `float`
        Low energy electron cutoff
    eebrk : `float`
        Break energy
    eehigh : `float`
        High energy electron cutoff
    p : `float`
        Slope below the break energy
    q : `float`
        Slope above the break energy
    z : `float`
        Mean atomic number of plasma
    efd : `bool`
        True - electron flux density distribution, False - electron density distribution. This
        input is not used in the main routine, but is passed to Brm_Fthin()

    Returns
    -------
    `tuple`
        (DmlinO, irer) Array of integral evaluation and array of error flags

    References
    ----------
    Initial version modified from SSW
    `Brm2_DmlinO <https://hesperia.gsfc.nasa.gov/ssw/packages/xray/idl/brm2/brm2_dmlino.pro>`_ and
    `Brm2_Dmlin <https://hesperia.gsfc.nasa.gov/ssw/packages/xray/idl/brm2/brm2_dmlin.pro>`_.

    """
    mc2 = const.get_constant('mc2')
    clight = const.get_constant('clight')

    if not eelow <= eebrk <= eehigh:
        raise ValueError(f'Condition eelow <= eebrek <= eehigh not satisfied '
                         f'({eelow}<={eebrk}<={eehigh}).')

    # Create arrays for integral sums and error flags.
    intsum1 = np.zeros_like(photon_energies, dtype=np.float64)
    ier1 = np.zeros_like(photon_energies, dtype=np.float64)
    intsum2 = np.zeros_like(photon_energies, dtype=np.float64)
    ier2 = np.zeros_like(photon_energies, dtype=np.float64)
    intsum3 = np.zeros_like(photon_energies, dtype=np.float64)
    ier3 = np.zeros_like(photon_energies, dtype=np.float64)

    P1 = np.where(photon_energies < eelow)[0]
    P2 = np.where(photon_energies < eebrk)[0]
    P3 = np.where(photon_energies <= eehigh)[0]

    # Part 1, below en_val[0] (usually eelow)
    if model == 'thick-target':
        if P1.size > 0:
            print('Part1')
            a_lg = np.log10(photon_energies[P1])
            b_lg = np.log10(np.full_like(a_lg, eelow))
            i = np.copy(P1)
            intsum1, ier1 = integrate_part(model=model,
                                           maxfcn=maxfcn,
                                           rerr=rerr,
                                           photon_energies=photon_energies,
                                           eelow=eelow,
                                           eebrk=eebrk,
                                           eehigh=eehigh,
                                           p=p,
                                           q=q,
                                           z=z,
                                           a_lg=a_lg,
                                           b_lg=b_lg,
                                           ll=i,
                                           efd=efd)

            # ier = 1 indicates no convergence.
            if sum(ier1):
                raise ValueError(
                    'Part 1 integral did not converge for some photon energies.'
                )

    # Part 2, between enval[0] and en_val[1](usually eelow and eebrk)

    aa = np.copy(photon_energies)
    if (P2.size > 0) and (eebrk > eelow):
        # TODO check if necessary as integration should only be carried out over point P2 which
        # by definition are not in P1
        if P1.size > 0:
            aa[P1] = eelow

        print('Part2')
        a_lg = np.log10(aa[P2])
        b_lg = np.log10(np.full_like(a_lg, eebrk))
        i = np.copy(P2)
        intsum2, ier2 = integrate_part(model=model,
                                       maxfcn=maxfcn,
                                       rerr=rerr,
                                       photon_energies=photon_energies,
                                       eelow=eelow,
                                       eebrk=eebrk,
                                       eehigh=eehigh,
                                       p=p,
                                       q=q,
                                       z=z,
                                       a_lg=a_lg,
                                       b_lg=b_lg,
                                       ll=i,
                                       efd=efd)

        if sum(ier2) > 0:
            raise ValueError(
                'Part 2 integral did not converge for some photon energies.')

    # Part 3: between eebrk and eehigh(usually eebrk and eehigh)
    aa = np.copy(photon_energies)
    if (P3.sum() > 0) and (eehigh > eebrk):
        if P2.size > 0:
            aa[P2] = eebrk

        print('Part3')
        a_lg = np.log10(aa[P3])
        b_lg = np.log10(np.full_like(a_lg, eehigh))
        i = np.copy(P3)
        intsum3, ier3 = integrate_part(model=model,
                                       maxfcn=maxfcn,
                                       rerr=rerr,
                                       photon_energies=photon_energies,
                                       eelow=eelow,
                                       eebrk=eebrk,
                                       eehigh=eehigh,
                                       p=p,
                                       q=q,
                                       z=z,
                                       a_lg=a_lg,
                                       b_lg=b_lg,
                                       ll=i,
                                       efd=efd)
        if sum(ier3) > 0:
            raise ValueError(
                'Part 3 integral did not converge for some photon energies.')

    # TODO check units here
    # Combine 3 parts and convert units and return
    if model == 'thick-target':
        DmlinO = (intsum1 + intsum2 + intsum3) * (mc2 / clight)
        ier = ier1 + ier2 + ier3
        return DmlinO, ier
    elif model == 'thin-target':
        Dmlin = (intsum2 + intsum3)
        ier = ier2 + ier3
        return Dmlin, ier
Exemple #6
0
def get_integrand(*,
                  model,
                  electron_energy,
                  photon_energy,
                  eelow,
                  eebrk,
                  eehigh,
                  p,
                  q,
                  z=1.2,
                  efd=True):
    """
    Return the value of the integrand for the thick- or thin-target bremsstrahlung models.

    Parameters
    ----------
    model : `str`
        Either `thick-target` or `thin-target`
    electron_energy : `numpy.array`
        Electron energies
    photon_energy : `numpy.array`
        Photon energies
    eelow : `float`
        Low energy electron cut off
    eebrk : `float`
        Break energy
    eehigh : `float`
        High energy cutoff
    p : `float`
        Slope below the break energy
    q : `flaot`
        Slope above the break energy
    z : `float`
        Mean atomic number of plasma
    efd: `bool` (optional)
        True (default) the electron flux distribution (electrons cm^-2 s^-1 keV^-1) is calculated
        with `~sunxspex.emission.BrokenPowerLawElectronDistribution.flux`. False, the electron
        density distribution (electrons cm^-3 keV^-1) is calculated with
        `~sunxspex.emission.BrokenPowerLawElectronDistribution.density`.

    Returns
    -------
    `numpy.array`
        The values of the integrand at the given electron_energies

    References
    ----------
    See SSW
    `brm2_fthin.pro <https://hesperia.gsfc.nasa.gov/ssw/packages/xray/idl/brm2/brm2_fthin.pro>`_ and
    `brm2_fouter.pro <https://hesperia.gsfc.nasa.gov/ssw/packages/xray/idl/brm2/brm2_fouter.pro>`_.

    """

    mc2 = const.get_constant('mc2')
    clight = const.get_constant('clight')
    gamma = (electron_energy / mc2) + 1.0
    brem_cross = bremsstrahlung_cross_section(electron_energy, photon_energy,
                                              z)
    collision_loss = collisional_loss(electron_energy)
    pc = np.sqrt(electron_energy * (electron_energy + 2.0 * mc2))
    electron_dist = BrokenPowerLawElectronDistribution(p=p,
                                                       q=q,
                                                       eelow=eelow,
                                                       eebrk=eebrk,
                                                       eehigh=eehigh)

    if model == 'thick-target':
        return electron_dist.density(
            electron_energy) * brem_cross * pc / collision_loss / gamma
    elif model == 'thin-target':
        if efd:
            # if electron flux distribution is assumed (default)
            return electron_dist.flux(electron_energy) * brem_cross * (mc2 /
                                                                       clight)
        else:
            # if electron density distribution is assumed
            # n_e * sigma * mc2 * (v / c)
            # TODO this is the same as IDL version but doesn't make sense as units are different?
            return electron_dist.flux(
                electron_energy) * brem_cross * pc / gamma
    else:
        raise ValueError(f"Given model: {model} is not one of supported values"
                         f"'thick-target', 'thin-target'")
Exemple #7
0
def bremsstrahlung_cross_section(electron_energy, photon_energy, z=1.2):
    """
    Compute the relativistic electron-ion bremsstrahlung cross section
    differential in energy (cm^2/mc^2 or 511 keV).

    Parameters
    ----------
    electron_energy : `numpy.array`
        Electron energies
    photon_energy : `numpy.array`
        Photon energies corresponding to electron_energy
    z : `float`
        Mean atomic number of target plasma

    Returns
    -------
    `np.array`
        The bremsstrahlung cross sections as a function of energy.

    Notes
    -----
    The cross section is from Equation (4) of [Haug]_. This closely follows Formula 3BN of [Koch]_,
    but requires fewer computational steps. The multiplicative factor introduced by [Elwert]_ is
    included.

    The initial version was heavily based of on [Brm_BremCross]_ from SSW IDL

    References
    ----------
    .. [Brm_BremCross] https://hesperia.gsfc.nasa.gov/ssw/packages/xray/idl/brm/brm_bremcross.pro
    .. [Haug] Haug, E., 1997, Astronomy and Astrophysics, 326, 417,
       `ADS <https://ui.adsabs.harvard.edu/abs/1997A%26A...326..417H/abstract>`__
    .. [Koch] Koch, H. W., & Motz, J. W., 1959, Reviews of Modern Physics, 31, 920,
       `ADS <https://ui.adsabs.harvard.edu/abs/1959RvMP...31..920K/abstract>`__
    .. [Elwert] Elwert, G. 1939, Annalen der Physik, 426, 178,
       `ADS <https://ui.adsabs.harvard.edu/abs/1939AnP...426..178E/abstract>`__
    """

    mc2 = const.get_constant('mc2')
    alpha = const.get_constant('alpha')
    twoar02 = const.get_constant('twoar02')

    # Numerical coefficients
    c11 = 4.0 / 3.0
    c12 = 7.0 / 15.0
    c13 = 11.0 / 70.0
    c21 = 7.0 / 20.0
    c22 = 9.0 / 28.0
    c23 = 263.0 / 210.0

    # Calculate normalised photon and total electron energies.
    if electron_energy.ndim == 2:
        k = np.expand_dims(photon_energy / mc2, axis=1)
    else:
        k = photon_energy / mc2
    e1 = (electron_energy / mc2) + 1.0

    # Calculate energies of scattered electrons and normalized momenta.
    e2 = e1 - k
    p1 = np.sqrt(e1**2 - 1.0)
    p2 = np.sqrt(e2**2 - 1.0)

    # Define frequently used quantities.
    e1e2 = e1 * e2
    p1p2 = p1 * p2
    p2sum = p1**2 + p2**2
    k2 = k**2
    e1e23 = e1e2**3
    pe = p2sum / e1e23

    # Define terms in cross section.
    ch1 = (c11 * e1e2 + k2) - (c12 * k2 / e1e2) - (c13 * k2 * pe / e1e2)
    ch2 = 1.0 + (1.0 / e1e2) + (c21 * pe) + (c22 * k2 + c23 * p1p2**2) / e1e23

    # Collect terms.
    crtmp = ch1 * (2.0 * np.log((e1e2 + p1p2 - 1.0) / k) - (p1p2 / e1e2) * ch2)
    crtmp = z**2 * crtmp / (k * p1**2)

    # Compute the Elwert factor.
    a1 = alpha * z * e1 / p1
    a2 = alpha * z * e2 / p2

    fe = (a2 / a1) * (1.0 - np.exp(-2.0 * np.pi * a1)) / (
        1.0 - np.exp(-2.0 * np.pi * a2))

    # Compute the differential cross section (units cm^2).
    cross_section = twoar02 * fe * crtmp

    return cross_section
Exemple #8
0
def bremsstrahlung_thin_target(photon_energies, ele_dist_type='bkpl', ele_dist_params=None, efd=True):
    """
    Computes the thin-target bremsstrahlung x-ray/gamma-ray spectrum from an isotropic electron
    distribution function provided in `broken_powerlaw`. The units of the computed flux is photons
    per second per keV per square centimeter.

    The electron flux distribution function is a double power law in electron energy with a
    low-energy cutoff and a high-energy cutoff.

    Parameters
    ----------
    photon_energies : `numpy.array`
        Array of photon energies to evaluate flux at
    ele_dist_type: str
        name of the electron distribution function
        - "bkpl": broken powerlaw
        - "kappa": kappa distribution
        - "discrete": discrete distribution given by an array of electron energy "E_ele"
         and differential electron distribution "dn/dE"
    ele_dist_params: ele_dist_params of the specific distribution
        - for powerlaw 'pl', params = (p, eelow, eehigh) (see below)
        - for broken powerlaw 'bkpl', params = (p, q, eelow, eebrk, eehigh)
            p   : float
                Slope below the break energy
            q   : float
                Slope above the break energy
            eelow : float
                Low energy electron cut off
            eebrk : float
                Break energy
            eehigh : float
                High energy electron cut off
        - for kappa, params (not implemented yet)
        - for discrete, params  = (electron_energy, electron_dist)
            electron_energy: np.array of electron energies. Unit: keV
            electron_dist: np.array of differential electron distribution at the given electron energy.
    efd : `bool`
        True (default) - input electron distribution is electron flux density distribution
        (unit electrons cm^-2 s^-1 keV^-1),
        False - input electron distribution is electron density distribution.
        (unit electrons cm^-3 keV^-1),
        This input is not used in the main routine, but is passed to brm2_dmlin and Brm2_Fthin

    Returns
    -------
    flux: `numpy.array`
        Multiplying the output of Brm2_ThinTarget by a0 gives an array of
        photon fluxes in photons s^-1 keV^-1 cm^-2, corresponding to the photon energies in the
        input array eph. The detector is assumed to be 1 AU rom the source. The coefficient a0 is
        calculated as a0 = nth * V * nnth, where nth: plasma density; cm^-3) V:
        volume of source; cm^3) nnth: Integrated nonthermal electron flux density (cm^-2 s^-1), if
        efd = True, or Integrated electron number density (cm^-3), if efd = False

    Notes
    -----
    If you want to plot the derivative of the flux, or the spectral index of the photon spectrum as
    a function of photon energy, you should set RERR to 1.d-6, because it is more sensitive to RERR
    than the flux.

    Adapted from SSW `Brm2_ThinTarget
    <https://hesperia.gsfc.nasa.gov/ssw/packages/xray/idl/brm2/brm2_thintarget.pro>`_
    """
    mc2 = const.get_constant('mc2')
    clight = const.get_constant('clight')
    au = const.get_constant('au')
    # Numerical coefficient for photo flux
    fcoeff = (clight / (4 * np.pi * au ** 2)) / mc2 ** 2.

    if ele_dist_type == 'bkpl':
        p = ele_dist_params[0]
        q = ele_dist_params[1]
        eelow = ele_dist_params[2]
        eebrk = ele_dist_params[3]
        eehigh = ele_dist_params[4]

        # Max number of points
        maxfcn = 2048
        # Average atomic number
        z = 1.2
        # Relative error
        rerr = 1e-4

        # Create arrays for the photon flux and error flags.
        flux = np.zeros_like(photon_energies, dtype=np.float64)
        iergq = np.zeros_like(photon_energies, dtype=np.float64)

        if eelow >= eehigh:
            raise ValueError('eehigh must be larger than eelow!')

        l, = np.where((photon_energies < eehigh) & (photon_energies > 0))
        if l.size > 0:
            flux[l], iergq[l] = split_and_integrate(model='thin-target',
                                                    photon_energies=photon_energies[l], maxfcn=maxfcn,
                                                    rerr=rerr, eelow=eelow, eebrk=eebrk, eehigh=eehigh,
                                                    p=p, q=q, z=z, efd=efd)

            flux *= fcoeff

            return flux
        else:
            raise Warning('The photon energies are higher than the highest electron energy or not '
                          'greater than zero')

    if ele_dist_type == 'discrete':
        electron_energy = ele_dist_params[0]
        electron_dist = ele_dist_params[1]
        flux = np.full_like(photon_energies, 0., dtype=np.float64)
        for i, eph in enumerate(photon_energies):
            # only electrons with energy above E_photon can contribute to the photon flux
            l, = np.where((electron_energy > eph))
            if l.size > 0:
                # calculate integrand
                gamma = (electron_energy[l] / mc2) + 1.0
                pc = np.sqrt(electron_energy[l] * (electron_energy[l] + 2.0 * mc2))
                brem_cross = bremsstrahlung_cross_section(electron_energy[l], eph)
                # calculate photon flux per electron energy bin
                if efd:
                    # if electron flux distribution is assumed (default)
                    flux_diff = electron_dist[l] * brem_cross * (mc2 / clight)
                else:
                    # if electron density distribution is assumed
                    flux_diff = electron_dist[l] * brem_cross * pc / gamma  # that is n_e * sigma * mc2 * (v / c)
                # now integrate the differential photon_flux with electron energy
                flux[i] = fcoeff * integrate.trapz(flux_diff, electron_energy[l])
        return flux