Exemplo n.º 1
0
def fluCalFun(flupara, flupara_sys, refpara, qz, sh=0):
    'takes in flupara, qz, return fluorescence data.'

    # unwrap fitting parameters
    qoff = flupara[0]  # q offset, in A^-1
    yscale = flupara[1]  # y scale, unitless
    bgcon = flupara[2]  # background constant, unitless.
    R = flupara[3] * 1e10  # surface curvature, in A
    if R == 0: R = 10000 * 1e10  # radius set to very big when curvature is "zero".
    conupbk = flupara[4]  # background linear is borrowed for upper phase concentration, in M
    surden = flupara[5]  # surface density, in A^-2
    conbulk = flupara[6]  # bulk concentration, in M

    # unwrap system parameters
    k0 = flupara_sys[1][0]
    mu = flupara_sys[3]
    bet = flupara_sys[4]
    del_ = flupara_sys[5]
    fluelepara = flupara_sys[6]
    slit = flupara_sys[7]  # size of slit1, in A
    detlen = flupara_sys[8]  # detector length, in A
    mu_top_inc, mu_top_emit, mu_bot_inc, mu_bot_emit = tuple(mu)
    bet_top_inc, bet_top_emit, bet_bot_inc, bet_bot_emit = tuple(bet)
    del_top_inc, del_top_emit, del_bot_inc, del_bot_emit = tuple(del_)

    # calculate reflectivity and footprint
    span = 75.6 * 1e7  # dimession of the sample call.
    qz = qz + qoff
    refModel = ref2min(refpara, None, None, None, fit=False, rrf=False)
    alpha = qz / k0 / 2  # incident angles
    fprint = slit / np.sin(alpha)  # get the footprints in unit of /AA
    center = - float(sh) * 1e7 / alpha
    if fprint[0] >= span:
        print 'Warning: footprint is %.2e, should not exceed 75.6e7' % fprint[0]

    # initialize fluorescence data, rows: total, aqueous, organic, interface
    absorb = lambda x: np.nan_to_num(np.exp(x))
    flu = np.zeros((6, len(alpha)))
    for i, a0 in enumerate(alpha):

        steps = int(fprint[i] / 0.5e6)  # use 0.1 mm as the step size
        stepsize = fprint[i] / steps

        # get the position of single ray hitting the surface
        x0 = np.linspace(center[i] - fprint[i] / 2, center[i] + fprint[i] / 2, steps)
        surface = np.array([gm.hit_surface([xx, 0], -a0, R, span) for xx in x0])
        miss = np.isinf(surface[:, 1])  # number of rays that miss the interface
        x_s = surface[~miss][:, 0]  # x' for rays that hit on the interface
        z_s = surface[~miss][:, 1]  # z' for rays that hit on the interface

        # sample raised up by sh is equivalent to beam moved toward upstream by sh/a0
        # hit_center_ray = gm.hit_surface([center[i], 0], -a0, R, np.inf)

        # (x,z) and other surface geometry for points where beam hit at the interface.
        theta = -x_s / R  # incline angle
        a_new = a0 + theta  # actual incident angle w.r. to the surface
        # a1 = a0 + 2 * theta
        a1 = a0
        x_inc = x_s + z_s / a0  # x position where the inc. xray passes z=0 line
        x_ref = x_s - z_s / a1  # x position where the inc. xray passes z=0 line.


        mu_eff_inc = mu_top_emit + mu_top_inc / a0  # eff.abs.depth for incident beam in oil phase
        mu_eff_ref = mu_top_emit - mu_top_inc / a1  # eff.abs.depth for reflected beam in water phase

        # z coordinate of the intersection of ray with following:
        z_inc_l = (x_inc + detlen / 2) * a0  # incidence with left det. boundary: x=-l/2
        z_inc_r = (x_inc - detlen / 2) * a0  # incidence with right det. boundary: x=l/2
        z_ref_l = -(x_ref + detlen / 2) * a1  # reflection with left det. boundary: x=-l/2
        z_ref_r = -(x_ref - detlen / 2) * a1  # reflection with right det. boundary: x=l/2

        # two regions: [-h/2a0,-l/2] & [-l/2,l/2]
        x_region = [(x_s <= -detlen / 2), (x_s > -detlen / 2) * (x_s < detlen / 2)]

        ################### for region x>= l/2  ########################
        x0_region1 = x0[surface[:, 0] > detlen / 2]  # choose x0 with x'>l/2
        upper_bulk1 = absorb(-x0_region1 * mu_top_inc) / mu_eff_inc * \
                      (absorb((x0_region1 + detlen / 2) * a0 * mu_eff_inc) -
                       absorb((x0_region1 - detlen / 2) * a0 * mu_eff_inc))

        # if beam miss the surface entirely, do the following:
        if len(x_s) == 0:  # the entire beam miss the interface, only incidence in upper phase.
            # sh_offset_factor = absorb(-mu_top_emit * center[i] * a0)
            sh_offset_factor = 1
            usum_inc = sh_offset_factor * stepsize * np.sum(upper_bulk1)
            flu[2, i] = yscale * usum_inc * N_A * conupbk * fluelepara[0][1] / 1e27  # oil phase incidence only
            flu[0, i] = flu[2, i]  # total intensity only contains oil phase
            continue
        else:
            ref = refModel(2 * k0 * a_new)  # calculate the reflectivity at incident angle alpha_prime.
            effd, trans = frsnllCal(del_top_inc, bet_top_inc, del_bot_inc, bet_bot_inc,
                                    mu_bot_emit, k0, a0, a_new)



        ################### for region -l/2 < x < l/2  #################
        lower_bulk2 = x_region[1] * absorb(-x_s * mu_top_inc - z_s / effd) * trans * effd * \
                      (absorb(z_s / effd) - absorb(z_inc_r / effd))
        surface = x_region[1] * trans * absorb(-mu_top_inc * x_s)
        upper_bulk2_inc = x_region[1] * \
                          (absorb(-x_inc * mu_top_inc) / mu_eff_inc * (
                                  absorb(z_inc_l * mu_eff_inc) - absorb(z_s * mu_eff_inc)))
        upper_bulk2_inc[np.isnan(upper_bulk2_inc)] = 0  # if there is nan, set to 0
        upper_bulk2_ref = x_region[1] * \
                          (absorb(-x_ref * mu_top_inc) / mu_eff_ref * ref * (
                                  absorb(z_ref_r * mu_eff_ref) - absorb(z_s * mu_eff_ref)))
        upper_bulk2_ref[np.isnan(upper_bulk2_ref)] = 0  # if there is nan, set to 0


        ###################### for region x<=-l/2 ########################
        lower_bulk3 = x_region[0] * absorb(-x_s * mu_top_inc - z_s / effd) * trans * effd * \
                      (absorb(z_inc_l / effd) - absorb(z_inc_r / effd))
        upper_bulk3 = x_region[0] * absorb(-x_ref * mu_top_inc) / mu_eff_ref * ref * \
                      (absorb(mu_eff_ref * z_ref_r) - absorb(mu_eff_ref * z_ref_l))


        # combine the two regions and integrate along x direction by performing np.sum.
        bsum = stepsize * np.sum(lower_bulk3 + lower_bulk2)
        ssum = stepsize * np.sum(surface)
        usum_inc = stepsize * (np.sum(upper_bulk1) + np.sum(upper_bulk2_inc))
        usum_ref = stepsize * (np.sum(upper_bulk3) + np.sum(upper_bulk2_ref))

        # vectorized integration method is proved to reduce the computation time by a factor of 5 to 10.
        int_bulk = yscale * bsum * N_A * conbulk * fluelepara[0][1] / 1e27
        int_upbk_inc = yscale * usum_inc * N_A * conupbk * fluelepara[0][1] / 1e27  # metal ions in the upper phase.
        int_upbk_ref = yscale * usum_ref * N_A * conupbk * fluelepara[0][1] / 1e27  # metal ions in the upper phase.
        int_sur = yscale * ssum * surden
        int_tot = int_bulk + int_sur + int_upbk_inc + int_upbk_ref + bgcon
        # total_ref = np.sum(ref>=0.999999)/float(len(ref)) # the ratio of footprint with total reflection
        total_ref = 1
        flu[:, i] = np.array([int_tot, int_bulk, int_upbk_inc, int_upbk_ref, int_sur, total_ref])
    return flu
Exemplo n.º 2
0
def fluCalFun_core(a0, sh, p):
    '''takes in flupara_fit, qz, return fluorescence data.
       Note that 'weights' contains the info of the steps for integration
       a0: the incident angle of X-ray beam, corrected with Qz_offset, in rad.
       sh: the sample height shift w.r.t. its norminal position, in A.
       p['wt']: weights for the beam profile, the length of which is the total amount of steps.
       p['k0']: wavevector for incident ray Energy, in KeV.
       p['detR']: detector range, in mm.
       p['hisc']: scale factor for the top phase, unitless.
       p['losc']: scale factor for the bottom pahse, unitless.
       p['bg']: background intensity, unitless.
       p['k1']: wavevector for emission ray energy, in KeV.
       p['tC']: ion concentration of the top phase, in M.
       p['bC']: ion concentration of the bottom phase, in M.
       p['sC']: ioin surface number density, in A^-2.
       p['qoff']: q off set for the data
       p['soff']: sample height offset (effoct of detector offset included) for the measurement
       p['l2off']: l2 offset for the measurement
       p['tRho']: electron density of top phase, in A^-3.
       p['bRho']: electron density of bottom phase, in A^-3.
       p['itMu']: mu for incident beam in top pahse, in A^-1
       p['etMu']: mu for emitted beam in top phass, in A^-1
       p['ibMu']: mu for incident beam in bottom pahse, in A^-1
       p['ebMu']: mu for emitted beam in bottom phass, in A^-1
       p['itBt']: beta for incident beam in top pahse, in A^-1
       p['etBt']: beta for emitted beam in top phass, in A^-1
       p['ibBt']: beta for incident beam in bottom pahse, in A^-1
       p['ebBt']: beta for emitted beam in bottom phass, in A^-1
       p['itDt']: delta for incident beam in top pahse, in A^-1
       p['etDt']: delta for emitted beam in top phass, in A^-1
       p['ibDt']: delta for incident beam in bottom pahse, in cm^-1
       p['ebDt']: delta for emitted beam in bottom phass, in cm^-1
       p['span']: the length of the sample cell, "the span", in A.
       p['curv']: the curvature of the interface, in A.
       p['bmsz']: the size of the beam for footprint calculation, in A.
       '''

    z_depth = -50  # the predifined depth of interfacial ions

    steps = len(p['wt']) - 1
    fprint = p['bmsz'] / np.sin(a0)  # foortprint of the beam on the interface.
    stepsize = fprint / steps
    center = -sh / a0

    # initialize fluorescence data, rows: total, aqueous, organic, interface

    # get the position of single ray hitting the surface
    x0 = np.linspace(center - fprint / 2, center + fprint / 2, steps + 1)
    surface = np.array(
        [gm.hit_surface([xx, 0], -a0, p['curv'], p['span']) for xx in x0])
    hit = np.isfinite(surface[:, 0]) * np.isfinite(
        surface[:, 1])  # rays that hit the liquid-liquid interface
    block = np.isfinite(surface[:, 0]) * np.isinf(
        surface[:, 1])  # rays that are blocked by the tray
    x_s = surface[:, 0][hit]  # x' for rays that hit on the interface
    z_s = surface[:, 1][hit]  # z' for rays that hit on the interface
    if p['curv'] == 1e14:
        z_s = np.zeros(x_s.shape)
    wt_s = p['wt'][hit]  # weight for rays that hit on the interface

    # (x,z) and other surface geometry for points where beam hit at the interface.
    theta = -x_s / p['curv']  # incident angle
    a_new = a0 + theta  # actual incident angle w.r. to the surface
    # a1 = a0 + 2 * theta
    a1 = a0
    x_inc = x_s + z_s / a0  # x position where the inc. xray passes z=0 line
    x_ref = x_s - z_s / a1  # x position where the ref. xray passes z=0 line.

    mu_eff_inc = p['etMu'] + p[
        'itMu'] / a0  # eff.abs.depth for incident beam in oil phase
    mu_eff_ref = p['etMu'] - p[
        'itMu'] / a1  # eff.abs.depth for reflected beam in water phase

    # z coordinate of the intersection of ray with following:
    z_inc_l = (x_inc +
               p['detR'] / 2) * a0  # incidence with left det. boundary: x=-l/2
    z_inc_r = (x_inc -
               p['detR'] / 2) * a0  # incidence with right det. boundary: x=l/2
    z_ref_l = -(x_ref + p['detR'] /
                2) * a1  # reflection with left det. boundary: x=-l/2
    z_ref_r = -(x_ref - p['detR'] /
                2) * a1  # reflection with right det. boundary: x=l/2

    # define the initial intensity to be just background value
    flu = np.array([p['bg']] * 6)
    # two regions: region3: [-h/2a0,-l/2] & region 2: [-l/2,l/2]
    x_region = [(x_s <= -p['detR'] / 2),
                (x_s > -p['detR'] / 2) * (x_s < p['detR'] / 2)]

    ################### for redgion 1: region x>= l/2  ########################
    # Special treatment for x>L/2 region.
    # This region only contains incident beam in organic phase if any, i.e. the part of beam before it hits
    # the interface. So the contribution from this part of the beam is calculated with x0 instead of x_s,
    # which is much easier.
    x0_region1 = x0[surface[:, 0] > p['detR'] / 2]  # choose x0 with x'>l/2
    wt_region1 = p['wt'][surface[:, 0] >
                         p['detR'] / 2]  # choose weight with x'>l/2
    upper_bulk1 = wt_region1 * \
                  absorb(-x0_region1 * p['itMu']) / mu_eff_inc * \
                  (absorb((x0_region1 + p['detR'] / 2) * a0 * mu_eff_inc) -
                   absorb((x0_region1 - p['detR'] / 2) * a0 * mu_eff_inc)) # Equation 5.6

    # The following case is that the entire beam misses the interface. In this case only incident beam is
    # calculated and all the calculation below this segment is skipped.
    if len(
            x_s
    ) == 0:  # the entire beam miss the interface, only incidence in upper phase.
        # sh_offset_factor = absorb(-mu_top_emit * center[i] * a0)
        usum_inc = stepsize * np.sum(upper_bulk1)
        flu[3] += p['hisc'] * usum_inc * N_A * p[
            'tC'] / 1e27  # oil phase incidence only + background
        flu[0] = flu[3]  # total intensity only contains oil phase
        return flu

    # If the beam hit the interface, reflectivity, transmissivity and the penetration depth is calculated.
    ref, trans, p_depth = fresnel(a_new, p['k0'], (p['itBt'], p['ibBt']),
                                  (p['itDt'], p['ibDt']))
    p_depth_eff = 1 / (p['ebMu'] + a_new / a0 / p_depth)

    ################### for region -l/2 < x < l/2  #################
    # original: uniform bulk distribution down to negative infinity.
    lower_bulk2 = x_region[1] * wt_s * absorb(-x_s * p['itMu'] - z_s / p_depth) * trans * p_depth * \
                  (absorb(z_s / p_depth_eff) - absorb(z_inc_r / p_depth_eff))
    # consider uniform bulk distribution down to 5nm below interface
    # lower_bulk2 = x_region[1] * wt_s * absorb(-x_s * p['itMu'] - z_s*a1/a0 / p_depth) * trans * p_depth_eff * \
    #              (absorb(z_s / p_depth_eff) - absorb(z_depth / p_depth_eff))
    # consider the interfacial region as z_depth below the interface (z_depth=0 for original model)
    # interface = x_region[1] * wt_s * trans * absorb(-p['itMu'] * x_s) * absorb(-a1/a0*z_depth/p_depth)
    interface = x_region[1] * wt_s * trans * absorb(-p['itMu'] * x_s)
    upper_bulk2_inc = x_region[1] * wt_s * \
                      (absorb(-x_inc * p['itMu']) / mu_eff_inc * (
                              absorb(z_inc_l * mu_eff_inc) - absorb(z_s * mu_eff_inc)))
    upper_bulk2_inc[np.isnan(upper_bulk2_inc)] = 0  # if there is nan, set to 0
    upper_bulk2_ref = x_region[1] * wt_s * \
                      (absorb(-x_ref * p['itMu']) / mu_eff_ref * ref * (
                              absorb(z_ref_r * mu_eff_ref) - absorb(z_s * mu_eff_ref)))
    upper_bulk2_ref[np.isnan(upper_bulk2_ref)] = 0  # if there is nan, set to 0

    ###################### for region x<=-l/2 ########################
    lower_bulk3 = x_region[0] * wt_s * absorb(-x_s * p['itMu'] - z_s / p_depth) * trans * p_depth_eff * \
                  (absorb(z_inc_l / p_depth_eff) - absorb(z_inc_r / p_depth_eff))
    upper_bulk3 = x_region[0] * wt_s * absorb(-x_ref * p['itMu']) / mu_eff_ref * ref * \
                  (absorb(mu_eff_ref * z_ref_r) - absorb(mu_eff_ref * z_ref_l))
    # if there are rays that are blocked by tray, their intensity is still significent
    if np.sum(block) > 0:
        edge = None
    # combine the two regions and integrate along x direction by performing np.sum.
    bsum = stepsize * np.sum(lower_bulk2 + lower_bulk3)
    ssum = stepsize * np.sum(interface)
    usum_inc = stepsize * (np.sum(upper_bulk1) + np.sum(upper_bulk2_inc))
    usum_ref = stepsize * (np.sum(upper_bulk3) + np.sum(upper_bulk2_ref))

    # vectorized integration method is proved to reduce the computation time by a factor of 5 to 10.
    int_bulk = p['losc'] * bsum * N_A * p['bC'] / 1e27
    int_upbk_inc = p['hisc'] * usum_inc * N_A * p[
        'tC'] / 1e27  # metal ions in the upper phase.
    int_upbk_ref = p['hisc'] * usum_ref * N_A * p[
        'tC'] / 1e27  # metal ions in the upper phase.
    int_sur = p['losc'] * ssum * p['sC']

    flu += np.array([
        int_bulk + int_sur + int_upbk_inc +
        int_upbk_ref,  # 2. total fluorescence + background
        int_bulk,  # 3. lower bulk
        int_sur,  # 4. interface
        int_upbk_inc + int_upbk_ref,  # 5. upper bulk
        int_upbk_inc,  # 6. upper bulk incidence
        int_upbk_ref
    ])  # 7. upper bulk reflection
    return flu
Exemplo n.º 3
0
def fluCalFun_core(a0, sh, p):
    '''takes in flupara_fit, qz, return fluorescence data.
       Note that 'weights' contains the info of the steps for integration
       a0: the incident angle of X-ray beam, corrected with Qz_offset, in rad.
       sh: the sample height shift w.r.s. to its norminal position, in A.
       p['wt']: weights for the beam profile, the length of which is the total amount of steps.
       p['k0']: wavevector for incident ray Energy, in KeV.
       p['detR']: detector range, in mm.
       p['hisc']: scale factor for the top phase, unitless.
       p['losc']: scale factor for the bottom pahse, unitless.
       p['bg']: background intensity, unitless.
       p['k1']: wavevector for emission ray energy, in KeV.
       p['tC']: ion concentration of the top phase, in M.
       p['bC']: ion concentration of the bottom phase, in M.
       p['sC']: ioin surface number density, in A^-2.
       p['qoff']: q off set for the data
       p['doff']: det range offset for the measurement
       p['l2off']: l2 offset for the measurement
       p['tRho']: electron density of top phase, in A^-3.
       p['bRho']: electron density of bottom phase, in A^-3.
       p['itMu']: mu for incident beam in top pahse, in cm^-1
       p['etMu']: mu for emitted beam in top phass, in cm^-1
       p['ibMu']: mu for incident beam in bottom pahse, in cm^-1
       p['ebMu']: mu for emitted beam in bottom phass, in cm^-1
       p['itBt']: beta for incident beam in top pahse, in cm^-1
       p['etBt']: beta for emitted beam in top phass, in cm^-1
       p['ibBt']: beta for incident beam in bottom pahse, in cm^-1
       p['ebBt']: beta for emitted beam in bottom phass, in cm^-1
       p['itDt']: delta for incident beam in top pahse, in cm^-1
       p['etDt']: delta for emitted beam in top phass, in cm^-1
       p['ibDt']: delta for incident beam in bottom pahse, in cm^-1
       p['ebDt']: delta for emitted beam in bottom phass, in cm^-1
       p['span']: the length of the sample cell, "the span", in A.
       p['curv']: the curvature of the interface, in A.
       p['bmsz']: the size of the beam for footprint calculation, in A.
       '''

    steps = len(p['wt']) - 1
    fprint = p['bmsz'] / np.sin(a0)  # foortprint of the beam on the interface.
    stepsize = fprint / steps
    center = -sh / a0

    # initialize fluorescence data, rows: total, aqueous, organic, interface
    absorb = lambda x: np.nan_to_num(np.exp(x))

    # get the position of single ray hitting the surface
    x0 = np.linspace(center - fprint / 2, center + fprint / 2, steps + 1)
    surface = np.array(
        [gm.hit_surface([xx, 0], -a0, p['curv'], p['span']) for xx in x0])
    miss = np.isinf(surface[:, 1])  # number of rays that miss the interface
    x_s = surface[:, 0][~miss]  # x' for rays that hit on the interface
    z_s = surface[:, 1][~miss]  # z' for rays that hit on the interface
    wt_s = p['wt'][~miss]  # weight for rays that hit on the interface

    # (x,z) and other surface geometry for points where beam hit at the interface.
    theta = -x_s / p['curv']  # incline angle
    a_new = a0 + theta  # actual incident angle w.r. to the surface
    # a1 = a0 + 2 * theta
    a1 = a0
    x_inc = x_s + z_s / a0  # x position where the inc. xray passes z=0 line
    x_ref = x_s - z_s / a1  # x position where the ref. xray passes z=0 line.

    mu_eff_inc = p['etMu'] + p[
        'itMu'] / a0  # eff.abs.depth for incident beam in oil phase
    mu_eff_ref = p['etMu'] - p[
        'itMu'] / a1  # eff.abs.depth for reflected beam in water phase

    # z coordinate of the intersection of ray with following:
    z_inc_l = (x_inc +
               p['detR'] / 2) * a0  # incidence with left det. boundary: x=-l/2
    z_inc_r = (x_inc -
               p['detR'] / 2) * a0  # incidence with right det. boundary: x=l/2
    z_ref_l = -(x_ref + p['detR'] /
                2) * a1  # reflection with left det. boundary: x=-l/2
    z_ref_r = -(x_ref - p['detR'] /
                2) * a1  # reflection with right det. boundary: x=l/2

    # two regions: [-h/2a0,-l/2] & [-l/2,l/2]
    x_region = [(x_s <= -p['detR'] / 2),
                (x_s > -p['detR'] / 2) * (x_s < p['detR'] / 2)]

    ################### for region x>= l/2  ########################
    x0_region1 = x0[surface[:, 0] > p['detR'] / 2]  # choose x0 with x'>l/2
    wt_region1 = p['wt'][surface[:, 0] >
                         p['detR'] / 2]  # choose weight with x'>l/2
    upper_bulk1 = wt_region1 * \
                  absorb(-x0_region1 * p['itMu']) / mu_eff_inc * \
                  (absorb((x0_region1 + p['detR'] / 2) * a0 * mu_eff_inc) -
                   absorb((x0_region1 - p['detR'] / 2) * a0 * mu_eff_inc))

    flu = np.array([0, 0, 0, 0, 0, 0])
    # if beam miss the surface entirely, do the following:
    if len(
            x_s
    ) == 0:  # the entire beam miss the interface, only incidence in upper phase.
        # sh_offset_factor = absorb(-mu_top_emit * center[i] * a0)
        usum_inc = stepsize * np.sum(upper_bulk1)
        flu[3] = p['hisc'] * usum_inc * N_A * p[
            'tC'] / 1e27  # oil phase incidence only
        flu[0] = flu[3]  # total intensity only contains oil phase
    else:
        ref = mfit.refCalFun([], [p['tRho'], p['bRho']],
                             [p['itMu'], p['ibMu']], [3.0],
                             2 * p['k0'] * a_new)
        p_depth, trans = penetrate((p['itBt'], p['ibBt']),
                                   (p['itDt'], p['ibDt']), a_new, p['k0'])
        p_depth_eff = 1 / (p['ebMu'] + a_new / a0 / p_depth)

        ################### for region -l/2 < x < l/2  #################
        lower_bulk2 = x_region[1] * wt_s * absorb(-x_s * p['itMu'] - z_s / p_depth) * trans * p_depth * \
                      (absorb(z_s / p_depth_eff) - absorb(z_inc_r / p_depth_eff))
        surface = x_region[1] * wt_s * trans * absorb(-p['itMu'] * x_s)
        upper_bulk2_inc = x_region[1] * wt_s * \
                          (absorb(-x_inc * p['itMu']) / mu_eff_inc * (
                                  absorb(z_inc_l * mu_eff_inc) - absorb(z_s * mu_eff_inc)))
        upper_bulk2_inc[np.isnan(
            upper_bulk2_inc)] = 0  # if there is nan, set to 0
        upper_bulk2_ref = x_region[1] * wt_s * \
                          (absorb(-x_ref * p['itMu']) / mu_eff_ref * ref * (
                                  absorb(z_ref_r * mu_eff_ref) - absorb(z_s * mu_eff_ref)))
        upper_bulk2_ref[np.isnan(
            upper_bulk2_ref)] = 0  # if there is nan, set to 0

        ###################### for region x<=-l/2 ########################
        lower_bulk3 = x_region[0] * wt_s * absorb(-x_s * p['itMu'] - z_s / p_depth) * trans * p_depth_eff * \
                      (absorb(z_inc_l / p_depth_eff) - absorb(z_inc_r / p_depth_eff))
        upper_bulk3 = x_region[0] * wt_s * absorb(-x_ref * p['itMu']) / mu_eff_ref * ref * \
                      (absorb(mu_eff_ref * z_ref_r) - absorb(mu_eff_ref * z_ref_l))

        # combine the two regions and integrate along x direction by performing np.sum.
        bsum = stepsize * np.sum(lower_bulk3 + lower_bulk2)
        ssum = stepsize * np.sum(surface)
        usum_inc = stepsize * (np.sum(upper_bulk1) + np.sum(upper_bulk2_inc))
        usum_ref = stepsize * (np.sum(upper_bulk3) + np.sum(upper_bulk2_ref))

        # vectorized integration method is proved to reduce the computation time by a factor of 5 to 10.
        int_bulk = p['losc'] * bsum * N_A * p['bC'] / 1e27
        int_upbk_inc = p['hisc'] * usum_inc * N_A * p[
            'tC'] / 1e27  # metal ions in the upper phase.
        int_upbk_ref = p['hisc'] * usum_ref * N_A * p[
            'tC'] / 1e27  # metal ions in the upper phase.
        int_sur = p['losc'] * ssum * p['sC']

        flu = np.array([
            int_bulk + int_sur + int_upbk_inc + int_upbk_ref +
            p['bg'],  # 2. total fluorescence
            int_bulk,  # 3. lower bulk
            int_sur,  # 4. interface
            int_upbk_inc + int_upbk_ref,  # 5. upper bulk
            int_upbk_inc,  # 6. upper bulk incidence
            int_upbk_ref
        ])  # 7. upper bulk reflection
    return flu