Module stasp.TimeLag

Expand source code
def time_lag(lc_v,m_v,percent_limit=90,auto=False):
    """
    Find the time lag between two light curves. 
    
    **Parameters**:

    `lc_v`: class: list of two 'Lightcurve'-objects    
        The light curves to be used in the coherence computation.
    
    `m_v`: np.ndarray    
        Array of number of segments to use, in increasing order.
        format: m_v[m_1,m_2,...], where type(m_i) = int, preferably a power of 2, and m_1 < m_2 etc.  
        
    `percent_limit`: float, optional, default: 90    
        Lower limit for percent of time bins having to be filled, i.e. without gaps, for that segment to be used.
    
    `auto`: boolean, default: False,
        If True, does not ask for user input (if want to clear all output and display time lag plot).
    
    **Returns**:

    `xf_v`: np.ndarray    
        Frequencies.
    
    `tau_v`: np.ndarray    
        Time lag. IMPORTANT NOTE: positive sign here means that hard leads (soft lags). Note, however,
        that the plot_time_lag-function makes clear what is what. Sorry for the confusion, but didn't want 
        to mess up somewhere by fixing the sign here and forgetting to fix it elsewhere.
    
    `dtau_v`: np.ndarray    
        Error in time lag.
        
    `num`: int
        The number of logarithmic frequency bins to create before plotting.
    """
    
    print('---------------------------------------------------------------------------------------------------')
    print('                           Computing the time lag...')
    print('---------------------------------------------------------------------------------------------------\n')
    start = timeit.default_timer()
        
    assert isinstance(lc_v[0], lightcurve) and isinstance(lc_v[1], lightcurve), 'The lc_v-input does not contain two light curve objects.'
    if len(m_v) >= 2:
        for i in range(0,len(m_v)-1):
            assert m_v[i] < m_v[i+1], 'Make sure that m_v[i] < m_v[i+1] for all i'
    
    # Make sure first lc is the one with lowest energy (the soft one); if not, switch place
    if lc_v[0].Emin > lc_v[1].Emin:
        print('Light curve with lowest energy was placed second in the list. I will switch and put it first.\n')
        lc_temp = copy.deepcopy(lc_v[0])
        lc_v[0] = copy.deepcopy(lc_v[1])
        lc_v[1] = lc_temp
    
    # Need to subtract if one lightcurve lies within the other
    lc_v = subtract_overlapping_energybands(lc_v)
    
    xf_v = np.array([])
    tau_v = np.array([])
    dtau_v = np.array([])
    dt = lc_v[0].dt
    
    freq_uplim = 1/(2*dt)
    for i in range(0,len(m_v)):
        m = m_v[i]
        print('Iteration {}) Computing using m = {} bins per segment, i.e. f in [{:.3f},{:.3f}]'.format(i+1,m,1/(m*dt),freq_uplim))
        print('---------------------------------------------------------------------------------------------------')
        
        # Find cross spectra
        ps_v, C_v = cross_spec(lc_v,m=m,percent_limit=percent_limit)

        # Coherence
        xf, gamma2, delta_gamma2, _ = coherence_noiseless(ps_v,m=m,C_v=C_v)
        
        K = np.size(C_v,axis=0) #number of segments 
        C = np.mean(C_v,axis=0) #mean cross spectra
        
        tau = np.angle(C)/(2*np.pi*xf)
        dtau = np.sqrt((1-gamma2)/(2*gamma2*K))/(2*np.pi*xf) 
        
        if freq_uplim != -1:
            print('Only use freq < {:.3f} to avoid to much overlaping.'.format(freq_uplim))
            tau = tau[xf < freq_uplim]
            dtau = dtau[xf < freq_uplim]
            xf = xf[xf < freq_uplim]
        
        xf_v = np.append(xf_v,xf)
        tau_v = np.append(tau_v,tau)
        dtau_v = np.append(dtau_v,dtau)
        
        # If len(m) >= 2, then we don't want the two computations to overlap too much... 
        # Now we make them overlap a factor 10 larger than the lowest frequency from the small m
        # E.g. if m = [2**8,2**17], dt = 0.002 --> freq_uplim = 1.96*10, meaning that even though 
        # m=2**17 covers f\in[0.0038,249] we only look at f\in[0.0038,19.6]. The high freq interval was
        # taken care of by m = [2**8].
        freq_uplim = np.amin(xf_v)*10
        print('Time lag for m = {} computed. \n'.format(m))
     
    time_taken = timeit.default_timer()-start
    print('---------------------------------------------------------------------------------------------------')
    print('                           Time lag found (in {:.2f} sec).'.format(time_taken))
    print('---------------------------------------------------------------------------------------------------')
        
    while True:
        if not auto:
            to_clear = input("Do you want to clear the standard out from prints [y/n]? ")
        else:
            to_clear = 'y'
        if to_clear not in ("n", "y"):
            print("Not an appropriate choice.")
        else:
            break
    
    if to_clear == 'y':
        clear_output(True)
        
    while True:
        if not auto:
            to_plot = input("Do you want to plot the time lag [y/n]? ")
        else:
            to_plot = 'n'
            
        if to_plot not in ("n", "y"):
            print("Not an appropriate choice.")
        else:
            break
        
    if to_plot == 'y': 
        Ebands = ('{}-{}'.format(lc_v[0].Emin,lc_v[0].Emax),'{}-{}'.format(lc_v[1].Emin,lc_v[1].Emax))
        plot_timelag(xf_v, tau_v, dtau_v,Ebands=Ebands)
        
    return xf_v, tau_v, dtau_v

def plot_timelag(xf_v,tau_v,dtau_v,Ebands=None,num=50,save_fig=False):
    """
    Plot the time lag.
    
    **Parameters**:

    `xf_v`: np.ndarray    
        Frequencies.
    
    `tau_v`: np.ndarray    
        Time lag.
    
    `dtau_v`: np.ndarray    
        Error in time lag.
        
    `Ebands`: tuple of strings, (Eband1,Eband2), optional, default: None    
        The energy bands of the two light curves compared.
    
    `num`: int, optional, default: 50
        The number of logarithmic frequency bins to create before plotting.
       
    `save_fig`: boolean, optional, default: False    
         If True, asks for path to location to save plot.
    """
    
    # Rebin
    xf_rebin,tau,dtau = log_rebin(xf_v, tau_v, dtau_v,num=num)
    
    ax = standard_plot()
    
    # Separate into pos/neg values of tau
    tau_n, tau_p = [-t for t in tau if t<0 ],[t for t in tau if t>0]
    x_n, x_p = [x for x,t in zip(xf_rebin,tau) if t<0],[x for x,t in zip(xf_rebin,tau) if t>0]
    dtau_n, dtau_p = [x for x,t in zip(dtau,tau) if t<0],[x for x,t in zip(dtau,tau) if t>0]
    
    # Loop over neg vs pos lags
    for x,tau,dtau,c,s,l in zip([x_n, x_p],[tau_n, tau_p],[dtau_n, dtau_p],['w','k'],[5,4.5],['soft leads','hard leads']):
        
        # Give label to first
        first = True
        
        # Replace errors that go below 0 with a downarrow
        for i in range(0,len(x)):
            
            if abs(dtau[i]) > abs(tau[i]): #if error is larger than tau-value
                # Plot lower error as an arrow
                ax.errorbar(x[i],tau[i],yerr=[[0.7*tau[i]],[0]], fmt = 'ok', uplims = True, mfc=c, markersize=s, elinewidth=1)
                # Plot upper error as normal
                ax.errorbar(x[i],tau[i],yerr=[[0],[dtau[i]]], fmt = 'ok', mfc=c, markersize=s, capsize=2, elinewidth=1, markeredgewidth=1)
            else:
                pass
                # Plot as normal
                if first:
                    ax.errorbar(x[i],tau[i],yerr=[[dtau[i]],[dtau[i]]], fmt = 'ok', mfc=c, markersize=s, capsize=2, elinewidth=1, markeredgewidth=1,label=l)
                    first = False
                else:
                    ax.errorbar(x[i],tau[i],yerr=[[dtau[i]],[dtau[i]]], fmt = 'ok', mfc=c, markersize=s, capsize=2, elinewidth=1, markeredgewidth=1)

    ax.set_xscale('log')
    ax.set_yscale('log')
    plt.legend()
    plt.xlabel('Frequency [Hz]')
    plt.ylabel('Lag (sec.)')
    
    if Ebands == None:
        Eband1 = input('First energyband in keV [?-?]): ')
        Eband2 = input('Second energyband in keV [?-?]: ')
    else:
        Eband1, Eband2 = Ebands[0], Ebands[1]
    ax.text(0.3,0.1,'({} keV) vs. ({} keV)'.format(Eband1,Eband2),fontsize=14,horizontalalignment='center', verticalalignment='center', transform=ax.transAxes)
    # Also good spot to place text:
    #ax.text(0.73,0.93,'({} keV) vs. ({} keV)'.format(first_band,second_band),fontsize=14,horizontalalignment='center', verticalalignment='center', transform=ax.transAxes)
    
    if save_fig:
        path = input('Path to location to save plot [end with .png]: ') 
        plt.savefig(path,bbox_inches='tight')
    plt.show()

Functions

def plot_timelag(xf_v, tau_v, dtau_v, Ebands=None, num=50, save_fig=False)

Plot the time lag.

Parameters:

xf_v: np.ndarray
Frequencies.

tau_v: np.ndarray
Time lag.

dtau_v: np.ndarray
Error in time lag.

Ebands: tuple of strings, (Eband1,Eband2), optional, default: None
The energy bands of the two light curves compared.

num: int, optional, default: 50 The number of logarithmic frequency bins to create before plotting.

save_fig: boolean, optional, default: False
If True, asks for path to location to save plot.

Expand source code
def plot_timelag(xf_v,tau_v,dtau_v,Ebands=None,num=50,save_fig=False):
    """
    Plot the time lag.
    
    **Parameters**:

    `xf_v`: np.ndarray    
        Frequencies.
    
    `tau_v`: np.ndarray    
        Time lag.
    
    `dtau_v`: np.ndarray    
        Error in time lag.
        
    `Ebands`: tuple of strings, (Eband1,Eband2), optional, default: None    
        The energy bands of the two light curves compared.
    
    `num`: int, optional, default: 50
        The number of logarithmic frequency bins to create before plotting.
       
    `save_fig`: boolean, optional, default: False    
         If True, asks for path to location to save plot.
    """
    
    # Rebin
    xf_rebin,tau,dtau = log_rebin(xf_v, tau_v, dtau_v,num=num)
    
    ax = standard_plot()
    
    # Separate into pos/neg values of tau
    tau_n, tau_p = [-t for t in tau if t<0 ],[t for t in tau if t>0]
    x_n, x_p = [x for x,t in zip(xf_rebin,tau) if t<0],[x for x,t in zip(xf_rebin,tau) if t>0]
    dtau_n, dtau_p = [x for x,t in zip(dtau,tau) if t<0],[x for x,t in zip(dtau,tau) if t>0]
    
    # Loop over neg vs pos lags
    for x,tau,dtau,c,s,l in zip([x_n, x_p],[tau_n, tau_p],[dtau_n, dtau_p],['w','k'],[5,4.5],['soft leads','hard leads']):
        
        # Give label to first
        first = True
        
        # Replace errors that go below 0 with a downarrow
        for i in range(0,len(x)):
            
            if abs(dtau[i]) > abs(tau[i]): #if error is larger than tau-value
                # Plot lower error as an arrow
                ax.errorbar(x[i],tau[i],yerr=[[0.7*tau[i]],[0]], fmt = 'ok', uplims = True, mfc=c, markersize=s, elinewidth=1)
                # Plot upper error as normal
                ax.errorbar(x[i],tau[i],yerr=[[0],[dtau[i]]], fmt = 'ok', mfc=c, markersize=s, capsize=2, elinewidth=1, markeredgewidth=1)
            else:
                pass
                # Plot as normal
                if first:
                    ax.errorbar(x[i],tau[i],yerr=[[dtau[i]],[dtau[i]]], fmt = 'ok', mfc=c, markersize=s, capsize=2, elinewidth=1, markeredgewidth=1,label=l)
                    first = False
                else:
                    ax.errorbar(x[i],tau[i],yerr=[[dtau[i]],[dtau[i]]], fmt = 'ok', mfc=c, markersize=s, capsize=2, elinewidth=1, markeredgewidth=1)

    ax.set_xscale('log')
    ax.set_yscale('log')
    plt.legend()
    plt.xlabel('Frequency [Hz]')
    plt.ylabel('Lag (sec.)')
    
    if Ebands == None:
        Eband1 = input('First energyband in keV [?-?]): ')
        Eband2 = input('Second energyband in keV [?-?]: ')
    else:
        Eband1, Eband2 = Ebands[0], Ebands[1]
    ax.text(0.3,0.1,'({} keV) vs. ({} keV)'.format(Eband1,Eband2),fontsize=14,horizontalalignment='center', verticalalignment='center', transform=ax.transAxes)
    # Also good spot to place text:
    #ax.text(0.73,0.93,'({} keV) vs. ({} keV)'.format(first_band,second_band),fontsize=14,horizontalalignment='center', verticalalignment='center', transform=ax.transAxes)
    
    if save_fig:
        path = input('Path to location to save plot [end with .png]: ') 
        plt.savefig(path,bbox_inches='tight')
    plt.show()
def time_lag(lc_v, m_v, percent_limit=90, auto=False)

Find the time lag between two light curves.

Parameters:

lc_v: class: list of two 'Lightcurve'-objects
The light curves to be used in the coherence computation.

m_v: np.ndarray
Array of number of segments to use, in increasing order. format: m_v[m_1,m_2,…], where type(m_i) = int, preferably a power of 2, and m_1 < m_2 etc.

percent_limit: float, optional, default: 90
Lower limit for percent of time bins having to be filled, i.e. without gaps, for that segment to be used.

auto: boolean, default: False, If True, does not ask for user input (if want to clear all output and display time lag plot).

Returns:

xf_v: np.ndarray
Frequencies.

tau_v: np.ndarray
Time lag. IMPORTANT NOTE: positive sign here means that hard leads (soft lags). Note, however, that the plot_time_lag-function makes clear what is what. Sorry for the confusion, but didn't want to mess up somewhere by fixing the sign here and forgetting to fix it elsewhere.

dtau_v: np.ndarray
Error in time lag.

num: int The number of logarithmic frequency bins to create before plotting.

Expand source code
def time_lag(lc_v,m_v,percent_limit=90,auto=False):
    """
    Find the time lag between two light curves. 
    
    **Parameters**:

    `lc_v`: class: list of two 'Lightcurve'-objects    
        The light curves to be used in the coherence computation.
    
    `m_v`: np.ndarray    
        Array of number of segments to use, in increasing order.
        format: m_v[m_1,m_2,...], where type(m_i) = int, preferably a power of 2, and m_1 < m_2 etc.  
        
    `percent_limit`: float, optional, default: 90    
        Lower limit for percent of time bins having to be filled, i.e. without gaps, for that segment to be used.
    
    `auto`: boolean, default: False,
        If True, does not ask for user input (if want to clear all output and display time lag plot).
    
    **Returns**:

    `xf_v`: np.ndarray    
        Frequencies.
    
    `tau_v`: np.ndarray    
        Time lag. IMPORTANT NOTE: positive sign here means that hard leads (soft lags). Note, however,
        that the plot_time_lag-function makes clear what is what. Sorry for the confusion, but didn't want 
        to mess up somewhere by fixing the sign here and forgetting to fix it elsewhere.
    
    `dtau_v`: np.ndarray    
        Error in time lag.
        
    `num`: int
        The number of logarithmic frequency bins to create before plotting.
    """
    
    print('---------------------------------------------------------------------------------------------------')
    print('                           Computing the time lag...')
    print('---------------------------------------------------------------------------------------------------\n')
    start = timeit.default_timer()
        
    assert isinstance(lc_v[0], lightcurve) and isinstance(lc_v[1], lightcurve), 'The lc_v-input does not contain two light curve objects.'
    if len(m_v) >= 2:
        for i in range(0,len(m_v)-1):
            assert m_v[i] < m_v[i+1], 'Make sure that m_v[i] < m_v[i+1] for all i'
    
    # Make sure first lc is the one with lowest energy (the soft one); if not, switch place
    if lc_v[0].Emin > lc_v[1].Emin:
        print('Light curve with lowest energy was placed second in the list. I will switch and put it first.\n')
        lc_temp = copy.deepcopy(lc_v[0])
        lc_v[0] = copy.deepcopy(lc_v[1])
        lc_v[1] = lc_temp
    
    # Need to subtract if one lightcurve lies within the other
    lc_v = subtract_overlapping_energybands(lc_v)
    
    xf_v = np.array([])
    tau_v = np.array([])
    dtau_v = np.array([])
    dt = lc_v[0].dt
    
    freq_uplim = 1/(2*dt)
    for i in range(0,len(m_v)):
        m = m_v[i]
        print('Iteration {}) Computing using m = {} bins per segment, i.e. f in [{:.3f},{:.3f}]'.format(i+1,m,1/(m*dt),freq_uplim))
        print('---------------------------------------------------------------------------------------------------')
        
        # Find cross spectra
        ps_v, C_v = cross_spec(lc_v,m=m,percent_limit=percent_limit)

        # Coherence
        xf, gamma2, delta_gamma2, _ = coherence_noiseless(ps_v,m=m,C_v=C_v)
        
        K = np.size(C_v,axis=0) #number of segments 
        C = np.mean(C_v,axis=0) #mean cross spectra
        
        tau = np.angle(C)/(2*np.pi*xf)
        dtau = np.sqrt((1-gamma2)/(2*gamma2*K))/(2*np.pi*xf) 
        
        if freq_uplim != -1:
            print('Only use freq < {:.3f} to avoid to much overlaping.'.format(freq_uplim))
            tau = tau[xf < freq_uplim]
            dtau = dtau[xf < freq_uplim]
            xf = xf[xf < freq_uplim]
        
        xf_v = np.append(xf_v,xf)
        tau_v = np.append(tau_v,tau)
        dtau_v = np.append(dtau_v,dtau)
        
        # If len(m) >= 2, then we don't want the two computations to overlap too much... 
        # Now we make them overlap a factor 10 larger than the lowest frequency from the small m
        # E.g. if m = [2**8,2**17], dt = 0.002 --> freq_uplim = 1.96*10, meaning that even though 
        # m=2**17 covers f\in[0.0038,249] we only look at f\in[0.0038,19.6]. The high freq interval was
        # taken care of by m = [2**8].
        freq_uplim = np.amin(xf_v)*10
        print('Time lag for m = {} computed. \n'.format(m))
     
    time_taken = timeit.default_timer()-start
    print('---------------------------------------------------------------------------------------------------')
    print('                           Time lag found (in {:.2f} sec).'.format(time_taken))
    print('---------------------------------------------------------------------------------------------------')
        
    while True:
        if not auto:
            to_clear = input("Do you want to clear the standard out from prints [y/n]? ")
        else:
            to_clear = 'y'
        if to_clear not in ("n", "y"):
            print("Not an appropriate choice.")
        else:
            break
    
    if to_clear == 'y':
        clear_output(True)
        
    while True:
        if not auto:
            to_plot = input("Do you want to plot the time lag [y/n]? ")
        else:
            to_plot = 'n'
            
        if to_plot not in ("n", "y"):
            print("Not an appropriate choice.")
        else:
            break
        
    if to_plot == 'y': 
        Ebands = ('{}-{}'.format(lc_v[0].Emin,lc_v[0].Emax),'{}-{}'.format(lc_v[1].Emin,lc_v[1].Emax))
        plot_timelag(xf_v, tau_v, dtau_v,Ebands=Ebands)
        
    return xf_v, tau_v, dtau_v