Example #1
0
def get_linear_growth(plot=False):
    '''
    Calculates an exponential growth rate based on transverse magnetic field
    energy.
    '''
    import pdb
    by         = cf.get_array('By') * 1e9
    bz         = cf.get_array('Bz') * 1e9
    
    mu0 = (4e-7) * np.pi             # Magnetic Permeability of Free Space (SI units)
    
    print('Fitting magnetic energy')
    bt  = np.sqrt(by ** 2 + bz ** 2)
    U_B = np.square(bt[:, 0])#.sum(axis=1)# * cf.NX * cf.dx / mu0 * 0.5
    #dU  = bk.get_derivative(U_B)
    
    #linear_cutoff = np.where(dU == dU.max())[0][0]
    
    #time_fit = cf.time_seconds_field[:linear_cutoff]
    plt.plot(cf.time_radperiods_field, by, marker='o')
    plt.xlim(0, 200)
# =============================================================================
#     fit_params = lmf.Parameters()
#     fit_params.add('amp'   , value=1.0            , min=None , max=None)
#     fit_params.add('growth', value=0.001*cf.gyfreq, min=0.0  , max=None)
#     
#     fit_output      = lmf.minimize(residual_exp, fit_params, args=(time_fit,), kws={'data': U_B[:linear_cutoff]},
#                                method='leastsq')
#     fit_function    = residual_exp(fit_output.params, time_fit)
# 
#     fit_dict        = fit_output.params.valuesdict()
# =============================================================================

# =============================================================================
#     if plot == True:
#         plt.ioff()
#         plt.figure()
#         plt.plot(cf.time_seconds_field[:linear_cutoff], U_B[:linear_cutoff], color='green', marker='o', label='Energy')
#         plt.plot(cf.time_seconds_field[:linear_cutoff], fit_function, color='b', label='Exp. fit')
#         plt.figtext(0.135, 0.725, r'$\gamma = %.3fs^{-1}$' % (fit_dict['growth'] / (2 * np.pi)))
#         plt.title('Transverse magnetic field energy')
#         plt.xlabel('Time (s)')
#         plt.ylabel('Energy (J)')
#         plt.legend()
#         
# # =============================================================================
# #         plt.figure()
# #         plt.plot(time_seconds[:linear_cutoff], dU[:linear_cutoff])
# # =============================================================================
#         
#         if plot == 'save':
#             save_path = cf.anal_dir + 'magnetic_energy_expfit.png'
#             plt.savefig(save_path)
#             plt.close('all')
#         elif plot == 'show':
#             plt.show()
#         else:
#             pass
# =============================================================================
    return
Example #2
0
def get_helical_components(overwrite=False, field='B'):
    temp_dir = cf.temp_dir
    print('Getting helical components for {} field'.format(field))
    if os.path.exists(temp_dir + '{}_positive_helicity.npy'.format(field)
                      ) == False or overwrite == True:
        ftime, Fy = cf.get_array('{}y'.format(field))
        ftime, Fz = cf.get_array('{}z'.format(field))

        Ft_pos = np.zeros((ftime.shape[0], cf.NX), dtype=np.complex128)
        Ft_neg = np.zeros((ftime.shape[0], cf.NX), dtype=np.complex128)

        for ii in range(Fy.shape[0]):
            print('Calculating helicity for field file', ii)
            Ft_pos[ii, :], Ft_neg[ii, :] = calculate_helicity(Fy[ii], Fz[ii])

        print('Saving {}-helicities to file'.format(field))
        np.save(temp_dir + '{}_positive_helicity.npy'.format(field), Ft_pos)
        np.save(temp_dir + '{}_negative_helicity.npy'.format(field), Ft_neg)
    else:
        print('Loading {}-elicities from file'.format(field))
        ftime, Fy = cf.get_array('{}y'.format(field))

        Ft_pos = np.load(temp_dir + '{}_positive_helicity.npy'.format(field))
        Ft_neg = np.load(temp_dir + '{}_negative_helicity.npy'.format(field))
    return ftime, Ft_pos, Ft_neg
Example #3
0
def get_FB_waves(overwrite=False, field='B', st=None, en=None):
    '''
    Use method of Shoji et al. (2011) to separate magnetic wave field into
    backwards and forwards components
     -- Should be equivalent to helicity thing
     -- Doesn't generalise well, can't tell between propagation direction and polarisation
     -- Only works for this because EMICs are generated in the L-mode and have single polarisation
     -- Don't cut off damping regions, she'll be right
    Fields are (time, space)
    
    FFT Positions
    # [0]     - Zero frequency term
    # [1:n/2] - Positive k terms
    # [n/2+1:] - Negative k terms
    # n/2 either both pos/neg nyquist (even) or 
    #             (odd) largest positive/negative frequency is on either side
    
    -- Do I have to account for that?
    -- Zeroing the A[0] term will probably just demean the resulting timeseries
    
    STILL NEEDS VALIDATION
     - Conservation: If I add the results, do I get the original waveform back?
     - Compare against existing helicity code, are they equivalent?
     
    The sign in F_perp (I think) just determines which is the +/- wave
    '''
    print('Calculating positive/negative helicities')
    ftime, Fy = cf.get_array('{}y'.format(field))
    ftime, Fz = cf.get_array('{}z'.format(field))

    F_perp = Fy[:, st:
                en] + 1j * Fz[:, st:
                              en]  # Should this be a +ve or -ve? Defines polarisation
    Nk = F_perp.shape[1]

    F_fwd = np.zeros(F_perp.shape, dtype=np.complex128)
    F_bwd = np.zeros(F_perp.shape, dtype=np.complex128)
    for ii in range(F_perp.shape[0]):
        Fk = np.fft.fft(F_perp[ii])
        Fk0 = 0.5 * Fk[0]

        fwd_fft = Fk.copy()  # Copy coefficients
        bwd_fft = Fk.copy()

        fwd_fft[Nk // 2 + 1:] *= 0.0  # Zero positive/negative wavenumbers
        bwd_fft[1:Nk // 2] *= 0.0

        fwd_fft[
            0] = Fk0  # Not sure what to do with the zero term. Split evenly?
        bwd_fft[0] = Fk0

        F_fwd[ii] = np.fft.ifft(
            fwd_fft)  # Transform back into spatial-domain data
        F_bwd[ii] = np.fft.ifft(bwd_fft)

    return ftime, F_fwd, F_bwd, F_perp
Example #4
0
def autopower_spectra(component='By',
                      overlap=0.5,
                      win_idx=None,
                      slide_idx=None,
                      df=50,
                      cell_idx=None):
    if 'B' in component.upper():
        ftime, y_arr = cf.get_array(component)  # Field component in nT
        y_arr = y_arr[:, cell_idx] * 1e9
    elif 'E' in component.upper():
        ftime, y_arr = cf.get_array(component)  # Field component in mV/m
        y_arr = y_arr[:, cell_idx] * 1e6
    else:
        sys.exit(
            'Field loading error for kwarg: component={}'.format(component))

    dt = cf.dt_field

    if win_idx is None:
        t_win = 1000. / df  # Window length (in seconds) for desired frequency resolution
        win_idx = int(np.ceil(t_win / cf.dt_field))  # Window size in indexes
        hlen = (
            win_idx - 1
        ) // 2  # Index of first mid-window, starting from idx 0. After this, displaced by slide_idx.

    if win_idx % 2 == 0:
        win_idx += 1  # Force window length to be odd number (for window-halving in FFT: Center values are always center values)

    if slide_idx is None:
        if overlap > 100:
            overlap /= 100.  # Check for accidental percentage instead of decimal overlap
        slide_idx = int(
            win_idx * (1. - overlap)
        )  # Calculate slide length in index values from overlap percentage

    num_slides = (
        y_arr.shape[0] - win_idx
    ) // slide_idx  # Count number of slides required. Unless multiple of idx_length, last value will not explicitly be computed

    FFT_output = do_stft(y_arr, win_idx, slide_idx,
                         num_slides)  # Do dynamic FFT
    FFT_times = (np.arange(num_slides) * slide_idx +
                 hlen) * dt  # Collect times for each FFT slice

    df = 1. / (win_idx * cf.dt_field)  # Frequency increment (in mHz)
    freq = np.asarray([df * jj for jj in range(win_idx // 2 + 1)
                       ])  # Frequency array up to Nyquist
    power = np.real(FFT_output * np.conj(FFT_output))
    return power, FFT_times, freq
Example #5
0
def plot_kt_winske(component='by'):
    qi = 1.602e-19  # Elementary charge (C)
    c = 3e8  # Speed of light (m/s)
    mp = 1.67e-27  # Mass of proton (kg)
    e0 = 8.854e-12  # Epsilon naught - permittivity of free space

    ftime, arr = cf.get_array(component)

    radperiods = ftime * cf.gyfreq
    gperiods = ftime / cf.gyperiod

    ts_folder = cf.anal_dir + '//winske_fourier_modes//'
    if os.path.exists(ts_folder) == False:
        os.makedirs(ts_folder)

    # Get first/last indices for FFT range and k-space array
    st = cf.ND
    if component[0].upper() == 'B':
        en = cf.ND + cf.NX
        k = np.fft.fftfreq(cf.NX, cf.dx)
    else:
        en = cf.ND + cf.NX
        k = np.fft.fftfreq(cf.NX, cf.dx)

    # Normalize to c/wpi
    cwpi = c / np.sqrt(cf.ne * qi**2 / (mp * e0))

    k *= cwpi
    k = k[k >= 0]
    kmax = k.shape[0]

    fft_matrix = np.zeros((arr.shape[0], en - st), dtype='complex128')
    for ii in range(arr.shape[0]):  # Take spatial FFT at each time, ii
        fft_matrix[ii, :] = np.fft.fft(arr[ii, st:en] - arr[ii, st:en].mean())

    kt = (fft_matrix[:, :k.shape[0]] *
          np.conj(fft_matrix[:, :k.shape[0]])).real

    plt.ioff()

    for ii in range(ftime.shape[0]):
        fig, ax = plt.subplots()
        ax.semilogy(k[1:kmax], kt[ii, 1:kmax], ds='steps-mid')
        ax.set_title('IT={:04d} :: T={:5.2f} :: GP={:5.2f}'.format(
            ii, radperiods[ii], gperiods[ii]),
                     family='monospace')
        ax.set_xlabel('K')
        ax.set_ylabel('BYK**2')
        ax.set_xlim(k[1], k[kmax - 1])
        fig.savefig(ts_folder +
                    'winske_fourier_{}_{}.png'.format(component, ii),
                    edgecolor='none')
        plt.close('all')

        sys.stdout.write(
            '\rCreating fourier mode plot for timestep {}'.format(ii))
        sys.stdout.flush()

    print('\n')
    return
Example #6
0
def get_growth_rates(do_plot=None):
    '''
    Extract the magnetic linear wave growth rate from:
        -- Fitting an exponential to the magnetic energy
        -- Fitting a growing sine wave to the field components at each cell
    
    The linear regime is calculated as all times before the maximum energy derivative,
    i.e. the growth is assumed exponential until the rate of energy transfer decreases.
    
    One could also take the min/max (i.e. abs) of the field through time and 
    fit an exponential to that, but it should be roughly equivalent to the energy fit.
    
    INPUT:
        -- do_plot : 'show', 'save' or 'None'. 'save' will also output growth rates to a text file.
    '''
    by  = cf.get_array('By') * 1e9
    bz  = cf.get_array('Bz') * 1e9
    
    linear_cutoff, gr_rate_energy   = fit_magnetic_energy(by, bz, plot=do_plot)
    freqs, power, max_idx           = get_max_frequency(by,       plot=do_plot)
    
    growth_rate_kt(by, linear_cutoff, freqs[max_idx])
    
    by_wamps, by_wfreqs, by_gr_rate = fit_field_component(by, freqs[max_idx], 'By', linear_cutoff, plot=do_plot)
    bz_wamps, bz_wfreqs, bz_gr_rate = fit_field_component(bz, freqs[max_idx], 'Bz', linear_cutoff, plot=do_plot)
    
    if do_plot == 'save':
        txt_path  = cf.anal_dir + 'growth_rates.txt'
        text_file = open(txt_path, 'w')
    else:
        text_file = None
    
    print('Energy growth rate: {}'.format(gr_rate_energy), file=text_file)
    print('By av. growth rate: {}'.format(by_gr_rate.mean()), file=text_file)
    print('Bz av. growth rate: {}'.format(bz_gr_rate.mean()), file=text_file)
    print('By min growth rate: {}'.format(by_gr_rate.min()), file=text_file)
    print('Bz min growth rate: {}'.format(bz_gr_rate.min()), file=text_file)
    print('By max growth rate: {}'.format(by_gr_rate.max()), file=text_file)
    print('Bz max growth rate: {}'.format(bz_gr_rate.max()), file=text_file)
    return
Example #7
0
def get_helical_components(overwrite):
    temp_dir = cf.temp_dir

    if os.path.exists(temp_dir + 'B_positive_helicity.npy') == False or overwrite == True:
        By = cf.get_array('By')
        Bz = cf.get_array('Bz')
        
        Bt_pos = np.zeros(By.shape, dtype=np.complex128)
        Bt_neg = np.zeros(By.shape, dtype=np.complex128)
        
        for ii in range(By.shape[0]):
            print('Analysing time step {}'.format(ii))
            Bt_pos[ii, :], Bt_neg[ii, :] = calculate_helicity(By[ii], Bz[ii], cf.NX, cf.dx)
        
        print('Saving helicities to file...')
        np.save(temp_dir + 'B_positive_helicity.npy', Bt_pos)
        np.save(temp_dir + 'B_negative_helicity.npy', Bt_neg)
    else:
        print('Loading helicities from file...')
        Bt_pos = np.load(temp_dir + 'B_positive_helicity.npy')
        Bt_neg = np.load(temp_dir + 'B_negative_helicity.npy')
    return Bt_pos, Bt_neg
Example #8
0
def get_wx(component):
    ftime, arr = cf.get_array(component)

    if component[0] == 'B':
        ncells = cf.NC + 1
    else:
        ncells = cf.NC

    if arr.shape[0] % 2 == 0:
        fft_matrix = np.zeros((arr.shape[0] // 2 + 1, ncells),
                              dtype='complex128')
    else:
        fft_matrix = np.zeros(((arr.shape[0] + 1) // 2, ncells),
                              dtype='complex128')

    for ii in range(arr.shape[1]):
        fft_matrix[:, ii] = np.fft.rfft(arr[:, ii] - arr[:, ii].mean())

    wx = (fft_matrix * np.conj(fft_matrix)).real
    return ftime, wx
Example #9
0
def get_kt(component):
    ftime, arr = cf.get_array(component)

    # Get first/last indices for FFT range and k-space array
    st = cf.ND
    if component[0].upper() == 'B':
        en = cf.ND + cf.NX + 1
        k = np.fft.fftfreq(cf.NX + 1, cf.dx)
    else:
        en = cf.ND + cf.NX
        k = np.fft.fftfreq(cf.NX, cf.dx)

    k = k[k >= 0]

    fft_matrix = np.zeros((arr.shape[0], en - st), dtype='complex128')
    for ii in range(arr.shape[0]):  # Take spatial FFT at each time, ii
        fft_matrix[ii, :] = np.fft.fft(arr[ii, st:en] - arr[ii, st:en].mean())

    kt = (fft_matrix[:, :k.shape[0]] *
          np.conj(fft_matrix[:, :k.shape[0]])).real

    return k, ftime, kt, st, en
Example #10
0
def get_wx(component, fac=1.0, normalize=True):
    ftime, arr = cf.get_array(component)
    arr *= fac

    if component[0].upper() == 'B':
        ncells = cf.NC + 1
    else:
        ncells = cf.NC

    if arr.shape[0] % 2 == 0:
        fft_matrix = np.zeros((arr.shape[0] // 2 + 1, ncells),
                              dtype='complex128')
    else:
        fft_matrix = np.zeros(((arr.shape[0] + 1) // 2, ncells),
                              dtype='complex128')

    for ii in range(arr.shape[1]):
        fft_matrix[:, ii] = np.fft.rfft(arr[:, ii] - arr[:, ii].mean())

    if normalize:
        fft_matrix *= 2.0 / ftime.shape[0]

    wx = (fft_matrix * np.conj(fft_matrix)).real
    return ftime, wx
Example #11
0
def get_wk(component):
    '''
    Spatial boundaries start at index
    '''
    ftime, arr = cf.get_array(component)

    if component.upper()[0] == 'B':
        st = cf.ND
        en = cf.ND + cf.NX + 1
    else:
        st = cf.ND
        en = cf.ND + cf.NX

    num_times = arr.shape[0]

    df = 1. / (num_times * cf.dt_field)
    dk = 1. / (cf.NX * cf.dx)

    f = np.arange(0, 1. / (2 * cf.dt_field), df)
    k = np.arange(0, 1. / (2 * cf.dx), dk)

    fft_matrix = np.zeros(arr[:, st:en].shape, dtype='complex128')
    fft_matrix2 = np.zeros(arr[:, st:en].shape, dtype='complex128')

    # Take spatial FFT at each time
    for ii in range(arr.shape[0]):
        fft_matrix[ii, :] = np.fft.fft(arr[ii, st:en] - arr[ii, st:en].mean())

    # Take temporal FFT at each position (now k)
    for ii in range(fft_matrix.shape[1]):
        fft_matrix2[:, ii] = np.fft.fft(fft_matrix[:, ii] -
                                        fft_matrix[:, ii].mean())

    wk = fft_matrix2[:f.shape[0], :k.shape[0]] * np.conj(
        fft_matrix2[:f.shape[0], :k.shape[0]])
    return k, f, wk
Example #12
0
def get_wk(component, linear_only=True, norm_z=False, centre_only=False):
    '''
    Spatial boundaries start at index.
    linear_only is kwarg to either take Boolean type or a number specifying up to
    what time to FFT up to (in units of wcinv)
    
    norm_z normalizes the field to the background magnetic field, if the input component
    is magnetic.
    
    centre_only :: kwarg to only FFT the middle __ % of cells. Calculated by 
    (1.0-centre_only)/2 indices on each side subtracted.
    '''
    ftime, arr = cf.get_array(component)
    if norm_z == True and component.upper()[0] == 'B':
        arr /= cf.B_eq

    t0 = 0
    if linear_only == True:
        # Find the point where magnetic energy peaks, FFT only up to 120% of that index
        yftime, by = cf.get_array('By')
        zftime, bz = cf.get_array('Bz')
        bt = np.sqrt(by**2 + bz**2)
        mu0 = (4e-7) * np.pi
        U_B = 0.5 / mu0 * np.square(bt[:, :]).sum(axis=1) * cf.dx
        max_idx = np.argmax(U_B)
        t1 = int(1.2 * max_idx)
        tf = ftime[t1]
    elif linear_only == False:
        t1 = ftime.shape[0]
        tf = ftime[t1 - 1]
    else:
        # Assume linear_only is a number
        end_sec = linear_only * 1. / cf.gyfreq
        diff_sec = np.abs(end_sec - ftime)
        t1 = np.where(diff_sec == diff_sec.min())[0][0]
        tf = ftime[t1]

    if centre_only != False:
        fraction_cut = (1.0 - centre_only) / 2.0
        if component.upper()[0] == 'B':
            cut = int((cf.x1B - cf.x0B) * fraction_cut)
        else:
            cut = int((cf.x1E - cf.x0E) * fraction_cut)
        print(f'Cutting {cut} indices from each side')
    else:
        cut = 0

    if component.upper()[0] == 'B':
        st = cf.x0B + cut
        en = cf.x1B - cut
    else:
        st = cf.x0E + cut
        en = cf.x1E - cut

    num_times = t1 - t0
    num_points = en - st

    df = 1. / (num_times * cf.dt_field)
    dk = 1. / (num_points * cf.dx)

    f = np.arange(0, 1. / (2 * cf.dt_field), df)
    k = np.arange(0, 1. / (2 * cf.dx), dk) * 2 * np.pi

    fft_matrix = np.zeros(arr[t0:t1, st:en].shape, dtype='complex128')
    fft_matrix2 = np.zeros(arr[t0:t1, st:en].shape, dtype='complex128')

    # Take spatial FFT at each time
    for ii in range(fft_matrix.shape[0]):
        fft_matrix[ii, :] = np.fft.fft(arr[t0 + ii, st:en] -
                                       arr[t0 + ii, st:en].mean())

    # Take temporal FFT at each position (now k)
    for ii in range(fft_matrix.shape[1]):
        fft_matrix2[:, ii] = np.fft.fft(fft_matrix[:, ii] -
                                        fft_matrix[:, ii].mean())

    wk = fft_matrix2[:f.shape[0], :k.shape[0]] * np.conj(
        fft_matrix2[:f.shape[0], :k.shape[0]])
    return k, f, wk, tf
Example #13
0
def plot_fourier_mode_timeseries(it_max=None):
    '''
    Load helical components Bt pos/neg, extract By_pos
    For each snapshot in time, take spatial FFT of By_pos (similar to how helicity is done)
    Save these snapshots in array
    Plot single mode timeseries from this 2D array
    
    Test run: Seems relatively close qualitatively, with a smaller growth rate
                and a few of the modes not quite as large. This could be any 
                number of reasons - from the simulation method to the helicity.
                Will be interesting to compare direct to linear theory via the
                method outlined in Munoz et al. (2018).
    '''
    ftime, By_raw = cf.get_array('By')
    ftime, Bz_raw = cf.get_array('Bz')
    radperiods = ftime * cf.gyfreq

    if it_max is None:
        it_max = ftime.shape[0]

    ftime, Bt_pos, Bt_neg = bk.get_helical_components()

    By_pos = Bt_pos.real
    x = np.linspace(0, cf.NX * cf.dx, cf.NX)
    k_modes = np.fft.rfftfreq(x.shape[0], d=cf.dx)
    Byk_2 = np.zeros((ftime.shape[0], k_modes.shape[0]), dtype=np.float64)

    # Do time loop here. Could also put normalization flag
    for ii in range(ftime.shape[0]):
        Byk = (1 / k_modes.shape[0]) * np.fft.rfft(By_pos[ii])
        Byk_2[ii, :] = (Byk * np.conj(Byk)).real / cf.B_eq**2

    plt.ioff()
    fig, axes = plt.subplots(ncols=2, nrows=3, sharex=True, figsize=(15, 10))

    axes[0, 0].semilogy(radperiods, Byk_2[:, 1])
    axes[0, 0].set_title('m = 1')
    axes[0, 0].set_xlim(0, 100)
    axes[0, 0].set_ylim(1e-7, 1e-3)

    axes[1, 0].semilogy(radperiods, Byk_2[:, 2])
    axes[1, 0].set_title('m = 2')
    axes[1, 0].set_xlim(0, 100)
    axes[1, 0].set_ylim(1e-6, 1e-1)

    axes[2, 0].semilogy(radperiods, Byk_2[:, 3])
    axes[2, 0].set_title('m = 3')
    axes[2, 0].set_xlim(0, 100)
    axes[2, 0].set_ylim(1e-6, 1e-1)

    axes[0, 1].semilogy(radperiods, Byk_2[:, 4])
    axes[0, 1].set_title('m = 4')
    axes[0, 1].set_xlim(0, 100)
    axes[0, 1].set_ylim(1e-6, 1e-0)

    axes[1, 1].semilogy(radperiods, Byk_2[:, 5])
    axes[1, 1].set_title('m = 5')
    axes[1, 1].set_xlim(0, 100)
    axes[1, 1].set_ylim(1e-6, 1e-0)

    axes[2, 1].semilogy(radperiods, Byk_2[:, 6])
    axes[2, 1].set_title('m = 6')
    axes[2, 1].set_xlim(0, 100)
    axes[2, 1].set_ylim(1e-6, 1e-0)

    fig.savefig(cf.anal_dir + 'fourier_modes.png')
    plt.close('all')

    #axes[ii].set_xlim(0, 100)
    #
    #axes[ii].set_xlabel('$\Omega_i t$')
    return
Example #14
0
def straight_line_fit(save=True, normalized_output=True, normfit_min=0.2, normfit_max=0.6):
    '''
    To do: Check units. Get growth rate from amplitude only? How to do across space
    -- Is wave power averaged/summed across space analogous to a single point? Or do I have to do single point?
    -- How to calculate growth rate of energy summed across space?
    -- Start with the simple and go from there. Saturation amplitudes have to match too?
    -- How do the Winske saturation amplitudes look? It might just be the Fu thing being fucky.
    '''
    print('Calculating growth rate...')
    ftime, by  = cf.get_array('By')
    ftime, bz  = cf.get_array('Bz')
    bt         = np.sqrt(by ** 2 + bz ** 2)
    btn        = np.square(bt[:, :]).sum(axis=1) / cf.B_eq ** 2
    mu0        = (4e-7) * np.pi
    U_B        = 0.5 / mu0 * np.square(bt[:, :]).sum(axis=1) * cf.dx
            
    max_idx = np.argmax(U_B)
    st = int(normfit_min * max_idx)
    en = int(normfit_max * max_idx)
    
    # Data to fit straight line to 
    linear_xvals = ftime[st:en]
    linear_yvals = np.log(U_B[st:en])
    
    gradient, y_intercept = np.polyfit(linear_xvals, linear_yvals, 1)

    # Calculate growth rate and normalize H to cyclotron frequency
    gamma         = 0.5*gradient
    normalized_gr = gamma / cf.gyfreq
    
    # Create line data to plot what we fitted
    linear_yfit = gradient * linear_xvals + y_intercept         # Returns y-values on log sscale
    log_yfit    = np.exp(linear_yfit)                           # Convert to linear values to use with semilogy()
    
    # Plot to check
    plt.ioff()
    fig, ax = plt.subplots(nrows=2, figsize=(15, 10), sharex=True)
      
    ax[0].set_title('Growth Rate :: $\gamma / \Omega_H$ = %.4f' % normalized_gr, fontsize=20)
    ax[0].plot(ftime, btn)
    ax[0].set_ylabel(r'$\frac{B^2}{B_0^2}$', rotation=0, labelpad=30, fontsize=18)
    ax[0].set_xlim(0, ftime[-1])
    ax[0].set_ylim(0, None)
    
    # Plot energy log scale
    ax[1].semilogy(ftime, U_B)
    ax[1].set_xlabel('Time (s)', fontsize=18)
    ax[1].set_ylabel('$U_B$', rotation=0, labelpad=30, fontsize=18)
    ax[1].set_xlim(0, ftime[-1])
    ax[1].set_ylim(None, None)
    
    # Mark growth rate indicators
    ax[1].scatter(ftime[max_idx], U_B[max_idx], c='r', s=20, marker='x')
    ax[1].scatter(ftime[st]     , U_B[st], c='r', s=20, marker='o')
    ax[1].scatter(ftime[en]     , U_B[en], c='r', s=20, marker='o')
    ax[1].semilogy(linear_xvals, log_yfit, c='r', ls='--', lw=2.0)
    
    if save == True:
        fig.savefig(cf.anal_dir + 'growth_rate_energy.png')
        plt.close('all')
    else:
        plt.show()
        
    if normalized_output == True:
        return normalized_gr
    else:
        return gamma
Example #15
0
def straight_line_fit_old(save=True, normalize_output=True, normalize_time=False,
                      normfit_min=0.2, normfit_max=0.6, plot_growth=True,
                      plot_LT=True, growth_only=True, klim=None, glim=0.25):
    '''
    To do: Check units. Get growth rate from amplitude only? How to do across space
    -- Is wave power averaged/summed across space analogous to a single point? Or do I have to do single point?
    -- How to calculate growth rate of energy summed across space?
    -- Start with the simple and go from there. Saturation amplitudes have to match too?
    -- How do the Winske saturation amplitudes look? It might just be the Fu thing being fucky.
    
    Idea : Sliding window for growth rates, calculate with 5 points and created instantaneous GR timeseries
    Why did all the growths turn green?
    '''
    if normalize_time == True:
        tfac = cf.gyfreq
        tlab = '$t \Omega_H$'
    else:
        tfac = 1.0
        tlab = 'Time (s)'
    
    print('Calculating growth rate...')
    ftime, by  = cf.get_array('By')
    ftime, bz  = cf.get_array('Bz')
    bt         = np.sqrt(by ** 2 + bz ** 2)
    btn        = np.square(bt[:, :]).sum(axis=1) / cf.B_eq ** 2
    mu0        = (4e-7) * np.pi
    U_B        = 0.5 / mu0 * np.square(bt[:, :]).sum(axis=1) * cf.dx
    
    if plot_growth == True:
        max_idx = np.argmax(U_B)
        st = int(normfit_min * max_idx)
        en = int(normfit_max * max_idx)
        
        # Data to fit straight line to 
        linear_xvals = ftime[st:en]
        linear_yvals = np.log(U_B[st:en])
        
        gradient, y_intercept = np.polyfit(linear_xvals, linear_yvals, 1)
    
        # Calculate growth rate and normalize H to cyclotron frequency
        gamma         = 0.5*gradient
        normalized_gr = gamma / cf.gyfreq
        
        # Create line data to plot what we fitted
        linear_yfit = gradient * linear_xvals * tfac + y_intercept  # Returns y-values on log sscale
        log_yfit    = np.exp(linear_yfit)                           # Convert to linear values to use with semilogy()
    else:
        gamma         = np.nan
        normalized_gr = np.nan
    
    
    # Plot showing magnetic field and energy with growth rate line superposed  
    plt.ioff()  
    fig, ax = plt.subplots(nrows=2, figsize=(15, 10), sharex=True)
    ofs = 5
    
    ax[0].set_title('Growth Rate :: $\gamma / \Omega_H$ = %.4f' % normalized_gr, fontsize=20)
    ax[0].plot(ftime*tfac, btn)
    ax[0].set_ylabel(r'$\frac{B^2}{B_0^2}$', rotation=0, labelpad=30, fontsize=18)
    ax[0].set_xlim(0, tfac*ftime[-1])
    ax[0].set_ylim(btn[ofs], None)
    
    # Plot energy log scale
    ax[1].semilogy(ftime*tfac, btn)
    ax[1].set_xlabel(tlab, fontsize=18)
    ax[1].set_ylabel(r'$\log_{10} \left(\frac{B^2}{B_0^2}\right)$', rotation=0, labelpad=30, fontsize=18)
    ax[1].set_xlim(0, tfac*ftime[-1])
    ax[1].set_ylim(U_B[ofs], None)
    
    if plot_growth == True:
        # Mark growth rate indicators
        ax[1].scatter(tfac*ftime[max_idx], U_B[max_idx], c='r', s=20, marker='x')
        ax[1].scatter(tfac*ftime[st]     , U_B[st], c='r', s=20, marker='o')
        ax[1].scatter(tfac*ftime[en]     , U_B[en], c='r', s=20, marker='o')
        ax[1].semilogy(linear_xvals, log_yfit, c='r', ls='--', lw=2.0)
    
    if plot_LT == True:
        # Calculate linear growth rates from simulation to compare
        # This could go into calculating gamma(k) later
        dk = 1. / (cf.NX * cf.dx)
        k  = np.arange(0, 1. / (2*cf.dx), dk) * 2*np.pi
        k_vals, CPDR_solns, WPDR_solns, HPDR_solns = disp.get_linear_dispersion_from_sim(k, zero_cold=False)
        k_vals *= 3e8 / cf.wpi
        
        CPDR_solns *= 2*np.pi / cf.gyfreq
        WPDR_solns *= 2*np.pi / cf.gyfreq
        HPDR_solns *= 2*np.pi / cf.gyfreq

        # Comparison of growth rate to linear theory
        clr = ['r', 'g', 'b']
        fig0, axes = plt.subplots(3, figsize=(15, 10), sharex=True)
        axes[0].set_title('Theoretical growth rates (Dashed: Simulation GR)')
        for ii in range(CPDR_solns.shape[1]):
            axes[0].plot(k_vals, CPDR_solns[:, ii].imag, c=clr[ii], label='Cold')
            axes[1].plot(k_vals, WPDR_solns[:, ii].imag, c=clr[ii], label='Warm')
            axes[2].plot(k_vals, HPDR_solns[:, ii].imag, c=clr[ii], label='Hot')
        for ax in axes:
            if np.isnan(normalized_gr) == False:
                ax.axhline(normalized_gr, ls='--', c='k', alpha=0.5)
            ax.set_xlim(0, klim)
            ax.legend()
            if growth_only == True:
                ax.set_ylim(0, None)
                if ax.get_ylim()[1] > glim:
                    ax.set_ylim(0, glim)
            ax.set_ylabel('$\gamma$', rotation=0)
        axes[-1].set_xlabel('$kc/\omega_{pi}$')
                    
    if save == True:
        fig.savefig( cf.anal_dir + 'growth_rate_energy.png')
        fig0.savefig(cf.anal_dir + 'growth_rate_energy_LT.png')
        plt.close('all')
    else:
        plt.show()
    return