Пример #1
0
def mms_load_feeps(trange=['2015-10-16', '2015-10-17'],
                   probe='1',
                   data_rate='srvy',
                   level='l2',
                   datatype='electron',
                   varformat=None,
                   varnames=[],
                   get_support_data=True,
                   suffix='',
                   time_clip=False,
                   no_update=False,
                   available=False,
                   notplot=False,
                   no_flatfield_corrections=False,
                   data_units=['count_rate', 'intensity'],
                   latest_version=False,
                   major_version=False,
                   min_version=None,
                   cdf_version=None,
                   spdf=False,
                   always_prompt=False):
    """
    This function loads FEEPS data into tplot variables
    
    Parameters
    ----------
        trange : list of str
            time range of interest [starttime, endtime] with the format 
            'YYYY-MM-DD','YYYY-MM-DD'] or to specify more or less than a day 
            ['YYYY-MM-DD/hh:mm:ss','YYYY-MM-DD/hh:mm:ss']

        probe : str or list of str
            list of probes, valid values for MMS probes are ['1','2','3','4']. 

        data_rate : str or list of str
            instrument data rates for FEEPS include ['brst', 'srvy']. The
            default is 'srvy'.

        level : str
            indicates level of data processing. the default if no level is specified is 'l2'

        datatype : str or list of str
            Valid datatypes for FEEPS are: 
                       L2, L1b: ['electron', 'ion']
                       L1a: ['electron-bottom', 'electron-top', 'ion-bottom', 'ion-top']

        get_support_data: bool
            Data with an attribute "VAR_TYPE" with a value of "support_data"
            will be loaded into tplot.  By default, only loads in data with a 
            "VAR_TYPE" attribute of "data".

        time_clip: bool
            Data will be clipped to the exact trange specified by the trange keyword.
            
        varformat: str
            The file variable formats to load into tplot.  Wildcard character
            "*" is accepted.  By default, all variables are loaded in.

        varnames: list of str
            List of variable names to load (if not specified,
            all data variables are loaded)

        suffix: str
            The tplot variable names will be given this suffix.  By default, 
            no suffix is added.

        notplot: bool
            If True, then data are returned in a hash table instead of 
            being stored in tplot variables (useful for debugging, and
            access to multi-dimensional data products)

        available: bool
            If True, simply return the available data files (without downloading)
            for the requested paramters

        no_update: bool
            Set this flag to preserve the original data. if not set and newer 
            data is found the existing data will be overwritten

        cdf_version: str
            Specify a specific CDF version # to load (e.g., cdf_version='4.3.0')

        min_version: str
            Specify a minimum CDF version # to load

        latest_version: bool
            Only grab the latest CDF version in the requested time interval

        major_version: bool
            Only open the latest major CDF version (e.g., X in vX.Y.Z) in the requested time interval

        always_prompt: bool
            Set this keyword to always prompt for the user's username and password;
            useful if you accidently save an incorrect password, or if your SDC password has changed

        spdf: bool
            If True, download the data from the SPDF instead of the SDC

    Returns:
        List of tplot variables created.

    """
    tvars = mms_load_data(trange=trange,
                          notplot=notplot,
                          probe=probe,
                          data_rate=data_rate,
                          level=level,
                          instrument='feeps',
                          datatype=datatype,
                          varformat=varformat,
                          varnames=varnames,
                          get_support_data=get_support_data,
                          suffix=suffix,
                          time_clip=time_clip,
                          no_update=no_update,
                          available=available,
                          latest_version=latest_version,
                          major_version=major_version,
                          min_version=min_version,
                          cdf_version=cdf_version,
                          spdf=spdf,
                          always_prompt=always_prompt)

    if tvars == [] or available or notplot or CONFIG['download_only']:
        return tvars

    probes = probe if isinstance(probe, list) else [probe]
    data_rates = data_rate if isinstance(data_rate, list) else [data_rate]
    levels = level if isinstance(level, list) else [level]
    datatypes = datatype if isinstance(datatype, list) else [datatype]
    data_units = data_units if isinstance(data_units, list) else [data_units]

    probes = [str(p) for p in probes]

    mms_feeps_correct_energies(probes, data_rate, level=level, suffix=suffix)

    if not no_flatfield_corrections:
        mms_feeps_flat_field_corrections(probes=probes,
                                         data_rate=data_rate,
                                         suffix=suffix)

    for probe in probes:
        for lvl in levels:
            for drate in data_rates:
                for datatype in datatypes:
                    mms_feeps_remove_bad_data(probe=probe,
                                              data_rate=drate,
                                              datatype=datatype,
                                              level=lvl,
                                              suffix=suffix)

                    for data_unit in data_units:
                        eyes = mms_feeps_active_eyes(trange, probe, drate,
                                                     datatype, lvl)

                        split_vars = mms_feeps_split_integral_ch(
                            data_unit,
                            datatype,
                            probe,
                            suffix=suffix,
                            data_rate=drate,
                            level=lvl,
                            sensor_eyes=eyes)

                        sun_removed_vars = mms_feeps_remove_sun(
                            eyes,
                            trange,
                            probe=probe,
                            datatype=datatype,
                            data_units=data_unit,
                            data_rate=drate,
                            level=lvl,
                            suffix=suffix)

                        omni_vars = mms_feeps_omni(eyes,
                                                   probe=probe,
                                                   datatype=datatype,
                                                   data_units=data_unit,
                                                   data_rate=drate,
                                                   level=lvl,
                                                   suffix=suffix)

                        tvars = tvars + split_vars + sun_removed_vars + omni_vars

                        tvars.append(
                            mms_feeps_spin_avg(probe=probe,
                                               data_units=data_unit,
                                               datatype=datatype,
                                               data_rate=drate,
                                               level=lvl,
                                               suffix=suffix))

    return tvars
Пример #2
0
def mms_feeps_pad(bin_size=16.3636, probe='1', energy=[70, 600], level='l2', suffix='', datatype='electron', data_units='intensity', data_rate='srvy', angles_from_bfield=False):

    # account for angular response (finite field of view) of instruments
    # electrons can use +/- 21.4 deg on each pitch angle as average response angle; ions can start with +/-10 deg, but both need to be further refined
    if datatype == 'electron':
        dangresp = 21.4 # deg
    elif datatype == 'ion': 
        dangresp = 10.0 # deg

    if energy[0] < 32.0:
        print('Please select a starting energy of 32 keV or above')
        return

    units_label = ''
    if data_units == 'intensity':
        units_label = '1/(cm^2-sr-s-keV)'
    elif data_units == 'counts':
        units_label = '[counts/s]'
        
    prefix = 'mms' + probe
    n_pabins = 180/bin_size
    pa_bins = [180.*pa_bin/n_pabins for pa_bin in range(0, int(n_pabins)+1)]
    pa_label = [180.*pa_bin/n_pabins+bin_size/2. for pa_bin in range(0, int(n_pabins))]

    if data_rate == 'brst' and angles_from_bfield == False:
        # v5.5+ = mms1_epd_feeps_srvy_l2_electron_pitch_angle
        pa_times, pa_data = get_data(prefix+'_epd_feeps_'+data_rate+'_'+level+'_'+datatype+'_pitch_angle'+suffix)
    else:
        pa_var, idx_maps = mms_feeps_pitch_angles(probe=probe, level=level, data_rate=data_rate, suffix=suffix)
        pa_times, pa_data = get_data(pa_var)

    if pa_data is None:
        print("Error, couldn't find the PA variable")
        return

    eyes = mms_feeps_active_eyes([pa_times.min(), pa_times.max()], probe, data_rate, datatype, level)

    pa_data_map = {}

    if data_rate == 'srvy':
        if datatype == 'electron': 
            pa_data_map['top-electron'] = idx_maps['electron-top']
            pa_data_map['bottom-electron'] = idx_maps['electron-bottom']
        if datatype == 'ion':
            pa_data_map['top-ion'] = idx_maps['electron-top']
            pa_data_map['bottom-ion'] = idx_maps['ion-bottom']
    elif data_rate == 'brst':
        # note: the following are indices of the top/bottom sensors in pa_data
        # they should be consistent with pa_dlimits.labels
        pa_data_map['top-electron'] = [0, 1, 2, 3, 4, 5, 6, 7, 8]
        pa_data_map['bottom-electron'] = [9, 10, 11, 12, 13, 14, 15, 16, 17]
        # and ions:
        pa_data_map['top-ion'] = [0, 1, 2]
        pa_data_map['bottom-ion'] = [3, 4, 5]

    sensor_types = ['top', 'bottom']

    if datatype == 'electron':
        dflux = np.zeros([len(pa_times), len(pa_data_map['top-electron'])+len(pa_data_map['bottom-electron'])])
        dpa = np.zeros([len(pa_times), len(pa_data_map['top-electron'])+len(pa_data_map['bottom-electron'])])
    elif datatype == 'ion':
        dflux = np.zeros([len(pa_times), len(pa_data_map['top-ion'])+len(pa_data_map['bottom-ion'])])
        dpa = np.zeros([len(pa_times), len(pa_data_map['top-ion'])+len(pa_data_map['bottom-ion'])])

    for s_type in sensor_types:
        pa_map = pa_data_map[s_type+'-'+datatype]
        particle_idxs = [eye-1 for eye in eyes[s_type]]
        for isen, sensor_num in enumerate(particle_idxs):
            var_name = 'mms'+str(probe)+'_epd_feeps_'+data_rate+'_'+level+'_'+datatype+'_'+s_type+'_'+data_units+'_sensorid_'+str(sensor_num+1)+'_clean_sun_removed'+suffix
            times, data, energies = get_data(var_name)
            data[data == 0] = 'nan' # remove any 0s before averaging
            # energy indices to use:
            indx = np.where((energies >= energy[0]) & (energies <= energy[1]))
            with warnings.catch_warnings():
                warnings.simplefilter("ignore", category=RuntimeWarning)
                dflux[:, pa_map[isen]] = np.nanmean(data[:, indx[0]], axis=1)
            dpa[:, pa_map[isen]] = pa_data[:, pa_map[isen]]

    # we need to replace the 0.0s left in after populating dpa with NaNs; these 
    # 0.0s are left in there because these points aren't covered by sensors loaded
    # for this datatype/data_rate
    dpa[dpa == 0] = 'nan'

    pa_flux = np.zeros([len(pa_times), int(n_pabins)])
    delta_pa = (pa_bins[1]-pa_bins[0])/2.0

    # Now loop through PA bins and time, find the telescopes where there is data in those bins and average it up!
    for pa_idx, pa_time in enumerate(pa_times):
        for ipa in range(0, int(n_pabins)):
            if not np.isnan(dpa[pa_idx, :][0]):
                ind = np.where((dpa[pa_idx, :] + dangresp >= pa_label[ipa]-delta_pa) & (dpa[pa_idx, :]-dangresp < pa_label[ipa]+delta_pa))
                if ind[0].size != 0:
                    with warnings.catch_warnings():
                        warnings.simplefilter("ignore", category=RuntimeWarning)
                        if len(ind[0]) > 1:
                            pa_flux[pa_idx, ipa] = np.nanmean(dflux[pa_idx, ind[0]], axis=0)
                        else:
                            pa_flux[pa_idx, ipa] = dflux[pa_idx, ind[0]]

    pa_flux[pa_flux == 0] = 'nan' # fill any missed bins with NAN

    en_range_string = str(int(energy[0])) + '-' + str(int(energy[1])) + 'keV'
    new_name = 'mms'+probe+'_epd_feeps_'+data_rate+'_'+level+'_'+datatype+'_'+data_units+'_'+ en_range_string +'_pad'+suffix

    store_data(new_name, data={'x': times, 'y': pa_flux, 'v': pa_label})
    options(new_name, 'ylog', False)
    options(new_name, 'zlog', True)
    options(new_name, 'spec', True)
    options(new_name, 'Colormap', 'jet')
    options(new_name, 'ztitle', units_label)
    options(new_name, 'ytitle', 'MMS' + str(probe) + ' ' + datatype + ' PA (deg)')

    # create the spin-averaged PAD
    spin_avg_var = mms_feeps_pad_spinavg(probe=probe, data_units=data_units, datatype=datatype, data_rate=data_rate, level=level, suffix=suffix, energy=energy)
    
    return [new_name, spin_avg_var]
Пример #3
0
def mms_feeps_pitch_angles(trange=None,
                           probe='1',
                           level='l2',
                           data_rate='srvy',
                           datatype='electron',
                           suffix=''):
    """
    Generates a tplot variable containing the FEEPS pitch angles for each telescope from magnetic field data.

    Parameters:
        trange : list of str
            time range of interest [starttime, endtime] with the format 
            'YYYY-MM-DD','YYYY-MM-DD'] or to specify more or less than a day 
            ['YYYY-MM-DD/hh:mm:ss','YYYY-MM-DD/hh:mm:ss']

        probe: str
            probe #, e.g., '4' for MMS4

        level: str
            data level, e.g., 'l2'

        data_rate: str
            instrument data rate, e.g., 'srvy' or 'brst'

        datatype: str
            'electron' or 'ion'

        suffix: str
            suffix of the loaded data

    Returns:
        Tuple: (tplot variable created, hash table used by PAD routine)
    """

    # get the times from the currently loaded FEEPS data
    times, pa_data = get_data('mms' + probe + '_epd_feeps_' + data_rate + '_' +
                              level + '_' + datatype + '_pitch_angle' + suffix)

    if times is not None:
        if trange is None:
            trange = [float(times.min()), float(times.max())]

    eyes = mms_feeps_active_eyes(trange, probe, data_rate, datatype, level)

    # need the B-field data
    mms_load_fgm(trange=trange,
                 probe=probe,
                 data_rate=data_rate,
                 varformat='*_b_bcs_*')

    btimes, Bbcs = get_data('mms' + probe + '_fgm_b_bcs_' + data_rate + '_l2')

    idx_maps = None

    # Rotation matrices for FEEPS coord system (FCS) into body coordinate system (BCS):
    Ttop = [[1. / np.sqrt(2.), -1. / np.sqrt(2.), 0],
            [1. / np.sqrt(2.), 1. / np.sqrt(2.), 0], [0, 0, 1]]
    Tbot = [[-1. / np.sqrt(2.), -1. / np.sqrt(2.), 0],
            [-1. / np.sqrt(2.), 1. / np.sqrt(2.), 0], [0, 0, -1]]

    # Telescope vectors in FCS:
    V1fcs = [0.347, -0.837, 0.423]
    V2fcs = [0.347, -0.837, -0.423]
    V3fcs = [0.837, -0.347, 0.423]
    V4fcs = [0.837, -0.347, -0.423]
    V5fcs = [-0.087, 0.000, 0.996]
    V6fcs = [0.104, 0.180, 0.978]
    V7fcs = [0.654, -0.377, 0.656]
    V8fcs = [0.654, -0.377, -0.656]
    V9fcs = [0.837, 0.347, 0.423]
    V10fcs = [0.837, 0.347, -0.423]
    V11fcs = [0.347, 0.837, 0.423]
    V12fcs = [0.347, 0.837, -0.423]

    if datatype == 'electron':
        pas = np.empty([len(btimes),
                        18])  # pitch angles for each eye at eaceh time

        # Telescope vectors in Body Coordinate System:
        #   Factors of -1 account for 180 deg shift between particle velocity and telescope normal direction:
        # Top:
        Vt1bcs = [
            -1. * (Ttop[0][0] * V1fcs[0] + Ttop[0][1] * V1fcs[1] +
                   Ttop[0][2] * V1fcs[2]),
            -1. * (Ttop[1][0] * V1fcs[0] + Ttop[1][1] * V1fcs[1] +
                   Ttop[1][2] * V1fcs[2]),
            -1. * (Ttop[2][0] * V1fcs[0] + Ttop[2][1] * V1fcs[1] +
                   Ttop[2][2] * V1fcs[2])
        ]
        Vt2bcs = [
            -1. * (Ttop[0][0] * V2fcs[0] + Ttop[0][1] * V2fcs[1] +
                   Ttop[0][2] * V2fcs[2]),
            -1. * (Ttop[1][0] * V2fcs[0] + Ttop[1][1] * V2fcs[1] +
                   Ttop[1][2] * V2fcs[2]),
            -1. * (Ttop[2][0] * V2fcs[0] + Ttop[2][1] * V2fcs[1] +
                   Ttop[2][2] * V2fcs[2])
        ]
        Vt3bcs = [
            -1. * (Ttop[0][0] * V3fcs[0] + Ttop[0][1] * V3fcs[1] +
                   Ttop[0][2] * V3fcs[2]),
            -1. * (Ttop[1][0] * V3fcs[0] + Ttop[1][1] * V3fcs[1] +
                   Ttop[1][2] * V3fcs[2]),
            -1. * (Ttop[2][0] * V3fcs[0] + Ttop[2][1] * V3fcs[1] +
                   Ttop[2][2] * V3fcs[2])
        ]
        Vt4bcs = [
            -1. * (Ttop[0][0] * V4fcs[0] + Ttop[0][1] * V4fcs[1] +
                   Ttop[0][2] * V4fcs[2]),
            -1. * (Ttop[1][0] * V4fcs[0] + Ttop[1][1] * V4fcs[1] +
                   Ttop[1][2] * V4fcs[2]),
            -1. * (Ttop[2][0] * V4fcs[0] + Ttop[2][1] * V4fcs[1] +
                   Ttop[2][2] * V4fcs[2])
        ]
        Vt5bcs = [
            -1. * (Ttop[0][0] * V5fcs[0] + Ttop[0][1] * V5fcs[1] +
                   Ttop[0][2] * V5fcs[2]),
            -1. * (Ttop[1][0] * V5fcs[0] + Ttop[1][1] * V5fcs[1] +
                   Ttop[1][2] * V5fcs[2]),
            -1. * (Ttop[2][0] * V5fcs[0] + Ttop[2][1] * V5fcs[1] +
                   Ttop[2][2] * V5fcs[2])
        ]
        Vt9bcs = [
            -1. * (Ttop[0][0] * V9fcs[0] + Ttop[0][1] * V9fcs[1] +
                   Ttop[0][2] * V9fcs[2]),
            -1. * (Ttop[1][0] * V9fcs[0] + Ttop[1][1] * V9fcs[1] +
                   Ttop[1][2] * V9fcs[2]),
            -1. * (Ttop[2][0] * V9fcs[0] + Ttop[2][1] * V9fcs[1] +
                   Ttop[2][2] * V9fcs[2])
        ]
        Vt10bcs = [
            -1. * (Ttop[0][0] * V10fcs[0] + Ttop[0][1] * V10fcs[1] +
                   Ttop[0][2] * V10fcs[2]),
            -1. * (Ttop[1][0] * V10fcs[0] + Ttop[1][1] * V10fcs[1] +
                   Ttop[1][2] * V10fcs[2]),
            -1. * (Ttop[2][0] * V10fcs[0] + Ttop[2][1] * V10fcs[1] +
                   Ttop[2][2] * V10fcs[2])
        ]
        Vt11bcs = [
            -1. * (Ttop[0][0] * V11fcs[0] + Ttop[0][1] * V11fcs[1] +
                   Ttop[0][2] * V11fcs[2]),
            -1. * (Ttop[1][0] * V11fcs[0] + Ttop[1][1] * V11fcs[1] +
                   Ttop[1][2] * V11fcs[2]),
            -1. * (Ttop[2][0] * V11fcs[0] + Ttop[2][1] * V11fcs[1] +
                   Ttop[2][2] * V11fcs[2])
        ]
        Vt12bcs = [
            -1. * (Ttop[0][0] * V12fcs[0] + Ttop[0][1] * V12fcs[1] +
                   Ttop[0][2] * V12fcs[2]),
            -1. * (Ttop[1][0] * V12fcs[0] + Ttop[1][1] * V12fcs[1] +
                   Ttop[1][2] * V12fcs[2]),
            -1. * (Ttop[2][0] * V12fcs[0] + Ttop[2][1] * V12fcs[1] +
                   Ttop[2][2] * V12fcs[2])
        ]
        # Bottom:
        Vb1bcs = [
            -1. * (Tbot[0][0] * V1fcs[0] + Tbot[0][1] * V1fcs[1] +
                   Tbot[0][2] * V1fcs[2]),
            -1. * (Tbot[1][0] * V1fcs[0] + Tbot[1][1] * V1fcs[1] +
                   Tbot[1][2] * V1fcs[2]),
            -1. * (Tbot[2][0] * V1fcs[0] + Tbot[2][1] * V1fcs[1] +
                   Tbot[2][2] * V1fcs[2])
        ]
        Vb2bcs = [
            -1. * (Tbot[0][0] * V2fcs[0] + Tbot[0][1] * V2fcs[1] +
                   Tbot[0][2] * V2fcs[2]),
            -1. * (Tbot[1][0] * V2fcs[0] + Tbot[1][1] * V2fcs[1] +
                   Tbot[1][2] * V2fcs[2]),
            -1. * (Tbot[2][0] * V2fcs[0] + Tbot[2][1] * V2fcs[1] +
                   Tbot[2][2] * V2fcs[2])
        ]
        Vb3bcs = [
            -1. * (Tbot[0][0] * V3fcs[0] + Tbot[0][1] * V3fcs[1] +
                   Tbot[0][2] * V3fcs[2]),
            -1. * (Tbot[1][0] * V3fcs[0] + Tbot[1][1] * V3fcs[1] +
                   Tbot[1][2] * V3fcs[2]),
            -1. * (Tbot[2][0] * V3fcs[0] + Tbot[2][1] * V3fcs[1] +
                   Tbot[2][2] * V3fcs[2])
        ]
        Vb4bcs = [
            -1. * (Tbot[0][0] * V4fcs[0] + Tbot[0][1] * V4fcs[1] +
                   Tbot[0][2] * V4fcs[2]),
            -1. * (Tbot[1][0] * V4fcs[0] + Tbot[1][1] * V4fcs[1] +
                   Tbot[1][2] * V4fcs[2]),
            -1. * (Tbot[2][0] * V4fcs[0] + Tbot[2][1] * V4fcs[1] +
                   Tbot[2][2] * V4fcs[2])
        ]
        Vb5bcs = [
            -1. * (Tbot[0][0] * V5fcs[0] + Tbot[0][1] * V5fcs[1] +
                   Tbot[0][2] * V5fcs[2]),
            -1. * (Tbot[1][0] * V5fcs[0] + Tbot[1][1] * V5fcs[1] +
                   Tbot[1][2] * V5fcs[2]),
            -1. * (Tbot[2][0] * V5fcs[0] + Tbot[2][1] * V5fcs[1] +
                   Tbot[2][2] * V5fcs[2])
        ]
        Vb9bcs = [
            -1. * (Tbot[0][0] * V9fcs[0] + Tbot[0][1] * V9fcs[1] +
                   Tbot[0][2] * V9fcs[2]),
            -1. * (Tbot[1][0] * V9fcs[0] + Tbot[1][1] * V9fcs[1] +
                   Tbot[1][2] * V9fcs[2]),
            -1. * (Tbot[2][0] * V9fcs[0] + Tbot[2][1] * V9fcs[1] +
                   Tbot[2][2] * V9fcs[2])
        ]
        Vb10bcs = [
            -1. * (Tbot[0][0] * V10fcs[0] + Tbot[0][1] * V10fcs[1] +
                   Tbot[0][2] * V10fcs[2]),
            -1. * (Tbot[1][0] * V10fcs[0] + Tbot[1][1] * V10fcs[1] +
                   Tbot[1][2] * V10fcs[2]),
            -1. * (Tbot[2][0] * V10fcs[0] + Tbot[2][1] * V10fcs[1] +
                   Tbot[2][2] * V10fcs[2])
        ]
        Vb11bcs = [
            -1. * (Tbot[0][0] * V11fcs[0] + Tbot[0][1] * V11fcs[1] +
                   Tbot[0][2] * V11fcs[2]),
            -1. * (Tbot[1][0] * V11fcs[0] + Tbot[1][1] * V11fcs[1] +
                   Tbot[1][2] * V11fcs[2]),
            -1. * (Tbot[2][0] * V11fcs[0] + Tbot[2][1] * V11fcs[1] +
                   Tbot[2][2] * V11fcs[2])
        ]
        Vb12bcs = [
            -1. * (Tbot[0][0] * V12fcs[0] + Tbot[0][1] * V12fcs[1] +
                   Tbot[0][2] * V12fcs[2]),
            -1. * (Tbot[1][0] * V12fcs[0] + Tbot[1][1] * V12fcs[1] +
                   Tbot[1][2] * V12fcs[2]),
            -1. * (Tbot[2][0] * V12fcs[0] + Tbot[2][1] * V12fcs[1] +
                   Tbot[2][2] * V12fcs[2])
        ]

        for i in range(0, 18):
            if i == 0:
                Vbcs = Vt1bcs
            if i == 1:
                Vbcs = Vt2bcs
            if i == 2:
                Vbcs = Vt3bcs
            if i == 3:
                Vbcs = Vt4bcs
            if i == 4:
                Vbcs = Vt5bcs
            if i == 5:
                Vbcs = Vt9bcs
            if i == 6:
                Vbcs = Vt10bcs
            if i == 7:
                Vbcs = Vt11bcs
            if i == 8:
                Vbcs = Vt12bcs
            if i == 9:
                Vbcs = Vb1bcs
            if i == 10:
                Vbcs = Vb2bcs
            if i == 11:
                Vbcs = Vb3bcs
            if i == 12:
                Vbcs = Vb4bcs
            if i == 13:
                Vbcs = Vb5bcs
            if i == 14:
                Vbcs = Vb9bcs
            if i == 15:
                Vbcs = Vb10bcs
            if i == 16:
                Vbcs = Vb11bcs
            if i == 17:
                Vbcs = Vb12bcs
            pas[:, i] = 180. / math.pi * np.arccos(
                (Vbcs[0] * Bbcs[:, 0] + Vbcs[1] * Bbcs[:, 1] +
                 Vbcs[2] * Bbcs[:, 2]) /
                (np.sqrt(Vbcs[0]**2 + Vbcs[1]**2 + Vbcs[2]**2) *
                 np.sqrt(Bbcs[:, 0]**2 + Bbcs[:, 1]**2 + Bbcs[:, 2]**2)))

            if data_rate == 'srvy':
                # the following 2 hash tables map TOP/BOTTOM telescope #s to index of the PA array created above
                top_tele_idx_map = {}
                bot_tele_idx_map = {}
                top_tele_idx_map[1] = 0
                top_tele_idx_map[2] = 1
                top_tele_idx_map[3] = 2
                top_tele_idx_map[4] = 3
                top_tele_idx_map[5] = 4
                top_tele_idx_map[9] = 5
                top_tele_idx_map[10] = 6
                top_tele_idx_map[11] = 7
                top_tele_idx_map[12] = 8
                bot_tele_idx_map[1] = 9
                bot_tele_idx_map[2] = 10
                bot_tele_idx_map[3] = 11
                bot_tele_idx_map[4] = 12
                bot_tele_idx_map[5] = 13
                bot_tele_idx_map[9] = 14
                bot_tele_idx_map[10] = 15
                bot_tele_idx_map[11] = 16
                bot_tele_idx_map[12] = 17

                top_idxs = []
                bot_idxs = []

                # PAs for only active eyes
                new_pas = np.empty([
                    len(btimes),
                    len(eyes['top']) + len(eyes['bottom'])
                ])  # pitch angles for each eye at eaceh time

                for top_idx, top_eye in enumerate(eyes['top']):
                    new_pas[:, top_idx] = pas[:, top_tele_idx_map[top_eye]]
                    top_idxs.append(top_idx)

                for bot_idx, bot_eye in enumerate(eyes['bottom']):
                    new_pas[:, bot_idx +
                            len(eyes['top'])] = pas[:,
                                                    bot_tele_idx_map[bot_eye]]
                    bot_idxs.append(bot_idx + len(eyes['top']))

                idx_maps = {
                    'electron-top': top_idxs,
                    'electron-bottom': bot_idxs
                }

            else:
                new_pas = pas

    elif datatype == 'ion':
        pas = np.empty([len(btimes),
                        6])  # pitch angles for each eye at each time

        # Telescope vectors in Body Coordinate System:
        #   Factors of -1 account for 180 deg shift between particle velocity and telescope normal direction:
        # Top:
        Vt6bcs = [
            -1. * (Ttop[0][0] * V6fcs[0] + Ttop[0][1] * V6fcs[1] +
                   Ttop[0][2] * V6fcs[2]),
            -1. * (Ttop[1][0] * V6fcs[0] + Ttop[1][1] * V6fcs[1] +
                   Ttop[1][2] * V6fcs[2]),
            -1. * (Ttop[2][0] * V6fcs[0] + Ttop[2][1] * V6fcs[1] +
                   Ttop[2][2] * V6fcs[2])
        ]
        Vt7bcs = [
            -1. * (Ttop[0][0] * V7fcs[0] + Ttop[0][1] * V7fcs[1] +
                   Ttop[0][2] * V7fcs[2]),
            -1. * (Ttop[1][0] * V7fcs[0] + Ttop[1][1] * V7fcs[1] +
                   Ttop[1][2] * V7fcs[2]),
            -1. * (Ttop[2][0] * V7fcs[0] + Ttop[2][1] * V7fcs[1] +
                   Ttop[2][2] * V7fcs[2])
        ]
        Vt8bcs = [
            -1. * (Ttop[0][0] * V8fcs[0] + Ttop[0][1] * V8fcs[1] +
                   Ttop[0][2] * V8fcs[2]),
            -1. * (Ttop[1][0] * V8fcs[0] + Ttop[1][1] * V8fcs[1] +
                   Ttop[1][2] * V8fcs[2]),
            -1. * (Ttop[2][0] * V8fcs[0] + Ttop[2][1] * V8fcs[1] +
                   Ttop[2][2] * V8fcs[2])
        ]
        # Bottom:
        Vb6bcs = [
            -1. * (Tbot[0][0] * V6fcs[0] + Tbot[0][1] * V6fcs[1] +
                   Tbot[0][2] * V6fcs[2]),
            -1. * (Tbot[1][0] * V6fcs[0] + Tbot[1][1] * V6fcs[1] +
                   Tbot[1][2] * V6fcs[2]),
            -1. * (Tbot[2][0] * V6fcs[0] + Tbot[2][1] * V6fcs[1] +
                   Tbot[2][2] * V6fcs[2])
        ]
        Vb7bcs = [
            -1. * (Tbot[0][0] * V7fcs[0] + Tbot[0][1] * V7fcs[1] +
                   Tbot[0][2] * V7fcs[2]),
            -1. * (Tbot[1][0] * V7fcs[0] + Tbot[1][1] * V7fcs[1] +
                   Tbot[1][2] * V7fcs[2]),
            -1. * (Tbot[2][0] * V7fcs[0] + Tbot[2][1] * V7fcs[1] +
                   Tbot[2][2] * V7fcs[2])
        ]
        Vb8bcs = [
            -1. * (Tbot[0][0] * V8fcs[0] + Tbot[0][1] * V8fcs[1] +
                   Tbot[0][2] * V8fcs[2]),
            -1. * (Tbot[1][0] * V8fcs[0] + Tbot[1][1] * V8fcs[1] +
                   Tbot[1][2] * V8fcs[2]),
            -1. * (Tbot[2][0] * V8fcs[0] + Tbot[2][1] * V8fcs[1] +
                   Tbot[2][2] * V8fcs[2])
        ]

        for i in range(0, 6):
            if i == 0:
                Vbcs = Vt6bcs
            if i == 1:
                Vbcs = Vt7bcs
            if i == 2:
                Vbcs = Vt8bcs
            if i == 3:
                Vbcs = Vb6bcs
            if i == 4:
                Vbcs = Vb7bcs
            if i == 5:
                Vbcs = Vb8bcs
            pas[:, i] = 180. / math.pi * np.arccos(
                (Vbcs[0] * Bbcs[:, 0] + Vbcs[1] * Bbcs[:, 1] +
                 Vbcs[2] * Bbcs[:, 2]) /
                (np.sqrt(Vbcs[0]**2 + Vbcs[1]**2 + Vbcs[2]**2) *
                 np.sqrt(Bbcs[:, 0]**2 + Bbcs[:, 1]**2 + Bbcs[:, 2]**2)))

        # the following 2 hash tables map TOP/BOTTOM telescope #s to index of the PA array created above
        top_tele_idx_map = {}
        bot_tele_idx_map = {}
        top_tele_idx_map[6] = 0
        top_tele_idx_map[7] = 1
        top_tele_idx_map[8] = 2
        bot_tele_idx_map[6] = 3
        bot_tele_idx_map[7] = 4
        bot_tele_idx_map[8] = 5

        top_idxs = []
        bot_idxs = []

        # PAs for only active eyes
        new_pas = np.empty([
            len(btimes), len(eyes['top']) + len(eyes['bottom'])
        ])  # pitch angles for each eye at eaceh time

        for top_idx, top_eye in enumerate(eyes['top']):
            new_pas[:, top_idx] = pas[:, top_tele_idx_map[top_eye]]
            top_idxs.append(top_idx)

        for bot_idx, bot_eye in enumerate(eyes['bottom']):
            new_pas[:, bot_idx +
                    len(eyes['top'])] = pas[:, bot_tele_idx_map[bot_eye]]
            bot_idxs.append(bot_idx + len(eyes['top']))

        idx_maps = {'ion-top': top_idxs, 'ion-bottom': bot_idxs}

    outvar = 'mms' + probe + '_epd_feeps_' + data_rate + '_' + level + '_' + datatype + '_pa' + suffix

    if data_exists(
            outvar):  # kludge for bug when the PAs were previously calculated
        return (outvar, idx_maps)

    store_data(outvar, data={'x': btimes, 'y': new_pas})

    # interpolate to the PA time stamps
    outdata = get_data(outvar, xarray=True)
    outdata_interpolated = outdata.interp({'time': times})

    store_data(outvar, data={'x': times, 'y': outdata_interpolated.values})

    return (outvar, idx_maps)
Пример #4
0
def mms_feeps_getgyrophase(trange=['2017-07-11/22:30', '2017-07-11/22:35'],
                           probe='2',
                           data_rate='brst',
                           level='l2',
                           datatype='electron'):
    """
    Calculates FEEPS gyrophase angles for electron burst data

    Notes: This is intended for use with burst mode data, but the output gyrophase product, 
           mms*_feeps_gyrophase, can be interpolated onto FEEPS burst or survey cadence
           to produce spectra

    Based on the IDL code writen by Drew Turner (10 Oct 2017): mms_feeps_getgyrophase.pro
    """
    mec_vars = mms.mec(trange=trange, probe=probe, data_rate=data_rate)
    if mec_vars is None:
        print(
            'Problem loading MEC data for calculating FEEPS gyrophase angles')

    qeci2sm = get_data('mms' + probe + '_mec_quat_eci_to_sm')
    qeci2bcs = get_data('mms' + probe + '_mec_quat_eci_to_bcs')
    rsun = get_data('mms' + probe + '_mec_r_sun_de421_eci')

    rsunbcs = np.zeros((len(rsun.times), 3))
    rduskbcs = np.zeros((len(rsun.times), 3))
    rdusksm = [0, 1, 0]

    for i in range(len(rsun.times)):
        q = qeci2bcs.y[i, :]
        # Quaternion rotation matrix:
        s = 1  # these quaternions are unit-qs
        R = np.array([
            [
                1 - 2 * s * (q[2]**2 + q[3]**2),
                2 * s * (q[1] * q[2] - q[3] * q[0]),
                2 * s * (q[1] * q[3] + q[2] * q[0])
            ],  # ECI to BCS
            [
                2 * s * (q[1] * q[2] + q[3] * q[0]),
                1 - 2 * s * (q[1]**2 + q[3]**2),
                2 * s * (q[2] * q[3] - q[1] * q[0])
            ],
            [
                2 * s * (q[1] * q[3] - q[2] * q[0]),
                2 * s * (q[2] * q[3] + q[1] * q[0]),
                1 - 2 * s * (q[1]**2 + q[2]**2)
            ]
        ])
        R = R.T
        rsunbcs[i, :] = np.array([
            R[0, 0] * rsun.y[i, 0] + R[1, 0] * rsun.y[i, 1] +
            R[2, 0] * rsun.y[i, 2], R[0, 1] * rsun.y[i, 0] +
            R[1, 1] * rsun.y[i, 1] + R[2, 1] * rsun.y[i, 2],
            R[0, 2] * rsun.y[i, 0] + R[1, 2] * rsun.y[i, 1] +
            R[2, 2] * rsun.y[i, 2]
        ])

        # now make second vector for gyroplane reference, dusk direction (+Y in SM)
        q = qeci2sm.y[i, :]
        # Quaternion rotation matrix:
        s = 1  # these quaternions are unit-qs
        R2 = np.array([
            [
                1 - 2 * s * (q[2]**2 + q[3]**2),
                2 * s * (q[1] * q[2] - q[3] * q[0]),
                2 * s * (q[1] * q[3] + q[2] * q[0])
            ],  # ECI to SM
            [
                2 * s * (q[1] * q[2] + q[3] * q[0]),
                1 - 2 * s * (q[1]**2 + q[3]**2),
                2 * s * (q[2] * q[3] - q[1] * q[0])
            ],
            [
                2 * s * (q[1] * q[3] - q[2] * q[0]),
                2 * s * (q[2] * q[3] + q[1] * q[0]),
                1 - 2 * s * (q[1]**2 + q[2]**2)
            ]
        ])
        # going from SM to ECI, so invert R:
        R2 = np.linalg.inv(R2)  # SM to ECI
        R2 = R2.T
        rduskeci = [
            R2[0, 0] * rdusksm[0] + R2[1, 0] * rdusksm[1] +
            R2[2, 0] * rdusksm[2], R2[0, 1] * rdusksm[0] +
            R2[1, 1] * rdusksm[1] + R2[2, 1] * rdusksm[2],
            R2[0, 2] * rdusksm[0] + R2[1, 2] * rdusksm[1] +
            R2[2, 2] * rdusksm[2]
        ]
        # Now convert to BCS:
        rduskbcs[i, :] = np.array([
            R[0, 0] * rduskeci[0] + R[1, 0] * rduskeci[1] +
            R[2, 0] * rduskeci[2], R[0, 1] * rduskeci[0] +
            R[1, 1] * rduskeci[1] + R[2, 1] * rduskeci[2],
            R[0, 2] * rduskeci[0] + R[1, 2] * rduskeci[1] +
            R[2, 2] * rduskeci[2]
        ])

    saved = store_data('mms' + probe + '_mec_r_sun_bcs',
                       data={
                           'x': rsun.times,
                           'y': rsunbcs
                       })
    if not saved:
        print('Problem saving r_sun_bcs')

    saved = store_data('mms' + probe + '_mec_r_dusk_bcs',
                       data={
                           'x': rsun.times,
                           'y': rduskbcs
                       })
    if not saved:
        print('Problem saving r_dusk_bcs')

    # Rotation matrices for FEEPS coord system (FCS) into body coordinate system (BCS):
    Ttop = np.array([[1. / np.sqrt(2.), -1. / np.sqrt(2.), 0],
                     [1. / np.sqrt(2.), 1. / np.sqrt(2.), 0], [0, 0, 1]]).T
    Tbot = np.array([[-1. / np.sqrt(2.), -1. / np.sqrt(2.), 0],
                     [-1. / np.sqrt(2.), 1. / np.sqrt(2.), 0], [0, 0, -1]]).T

    # Telescope vectors in FCS:
    # Electrons
    V1fcs = [0.347, -0.837, 0.423]
    V2fcs = [0.347, -0.837, -0.423]
    V3fcs = [0.837, -0.347, 0.423]
    V4fcs = [0.837, -0.347, -0.423]
    V5fcs = [-0.087, 0.000, 0.996]
    V9fcs = [0.837, 0.347, 0.423]
    V10fcs = [0.837, 0.347, -0.423]
    V11fcs = [0.347, 0.837, 0.423]
    V12fcs = [0.347, 0.837, -0.423]
    # Ions
    V6fcs = [0.104, 0.180, 0.978]
    V7fcs = [0.654, -0.377, 0.656]
    V8fcs = [0.654, -0.377, -0.656]

    # Now telescope vectors in Body Coordinate System:
    #   Factors of -1 account for 180 deg shift between particle velocity and telescope normal direction:
    # Top:
    Vt1bcs = [
        -1. * (Ttop[0, 0] * V1fcs[0] + Ttop[1, 0] * V1fcs[1] +
               Ttop[2, 0] * V1fcs[2]),
        -1. * (Ttop[0, 1] * V1fcs[0] + Ttop[1, 1] * V1fcs[1] +
               Ttop[2, 1] * V1fcs[2]), -1. *
        (Ttop[0, 2] * V1fcs[0] + Ttop[1, 2] * V1fcs[1] + Ttop[2, 2] * V1fcs[2])
    ]
    Vt2bcs = [
        -1. * (Ttop[0, 0] * V2fcs[0] + Ttop[1, 0] * V2fcs[1] +
               Ttop[2, 0] * V2fcs[2]),
        -1. * (Ttop[0, 1] * V2fcs[0] + Ttop[1, 1] * V2fcs[1] +
               Ttop[2, 1] * V2fcs[2]), -1. *
        (Ttop[0, 2] * V2fcs[0] + Ttop[1, 2] * V2fcs[1] + Ttop[2, 2] * V2fcs[2])
    ]
    Vt3bcs = [
        -1. * (Ttop[0, 0] * V3fcs[0] + Ttop[1, 0] * V3fcs[1] +
               Ttop[2, 0] * V3fcs[2]),
        -1. * (Ttop[0, 1] * V3fcs[0] + Ttop[1, 1] * V3fcs[1] +
               Ttop[2, 1] * V3fcs[2]), -1. *
        (Ttop[0, 2] * V3fcs[0] + Ttop[1, 2] * V3fcs[1] + Ttop[2, 2] * V3fcs[2])
    ]
    Vt4bcs = [
        -1. * (Ttop[0, 0] * V4fcs[0] + Ttop[1, 0] * V4fcs[1] +
               Ttop[2, 0] * V4fcs[2]),
        -1. * (Ttop[0, 1] * V4fcs[0] + Ttop[1, 1] * V4fcs[1] +
               Ttop[2, 1] * V4fcs[2]), -1. *
        (Ttop[0, 2] * V4fcs[0] + Ttop[1, 2] * V4fcs[1] + Ttop[2, 2] * V4fcs[2])
    ]
    Vt5bcs = [
        -1. * (Ttop[0, 0] * V5fcs[0] + Ttop[1, 0] * V5fcs[1] +
               Ttop[2, 0] * V5fcs[2]),
        -1. * (Ttop[0, 1] * V5fcs[0] + Ttop[1, 1] * V5fcs[1] +
               Ttop[2, 1] * V5fcs[2]), -1. *
        (Ttop[0, 2] * V5fcs[0] + Ttop[1, 2] * V5fcs[1] + Ttop[2, 2] * V5fcs[2])
    ]
    Vt6bcs = [
        -1. * (Ttop[0, 0] * V6fcs[0] + Ttop[1, 0] * V6fcs[1] +
               Ttop[2, 0] * V6fcs[2]),
        -1. * (Ttop[0, 1] * V6fcs[0] + Ttop[1, 1] * V6fcs[1] +
               Ttop[2, 1] * V6fcs[2]), -1. *
        (Ttop[0, 2] * V6fcs[0] + Ttop[1, 2] * V6fcs[1] + Ttop[2, 2] * V6fcs[2])
    ]
    Vt7bcs = [
        -1. * (Ttop[0, 0] * V7fcs[0] + Ttop[1, 0] * V7fcs[1] +
               Ttop[2, 0] * V7fcs[2]),
        -1. * (Ttop[0, 1] * V7fcs[0] + Ttop[1, 1] * V7fcs[1] +
               Ttop[2, 1] * V7fcs[2]), -1. *
        (Ttop[0, 2] * V7fcs[0] + Ttop[1, 2] * V7fcs[1] + Ttop[2, 2] * V7fcs[2])
    ]
    Vt8bcs = [
        -1. * (Ttop[0, 0] * V8fcs[0] + Ttop[1, 0] * V8fcs[1] +
               Ttop[2, 0] * V8fcs[2]),
        -1. * (Ttop[0, 1] * V8fcs[0] + Ttop[1, 1] * V8fcs[1] +
               Ttop[2, 1] * V8fcs[2]), -1. *
        (Ttop[0, 2] * V8fcs[0] + Ttop[1, 2] * V8fcs[1] + Ttop[2, 2] * V8fcs[2])
    ]
    Vt9bcs = [
        -1. * (Ttop[0, 0] * V9fcs[0] + Ttop[1, 0] * V9fcs[1] +
               Ttop[2, 0] * V9fcs[2]),
        -1. * (Ttop[0, 1] * V9fcs[0] + Ttop[1, 1] * V9fcs[1] +
               Ttop[2, 1] * V9fcs[2]), -1. *
        (Ttop[0, 2] * V9fcs[0] + Ttop[1, 2] * V9fcs[1] + Ttop[2, 2] * V9fcs[2])
    ]
    Vt10bcs = [
        -1. * (Ttop[0, 0] * V10fcs[0] + Ttop[1, 0] * V10fcs[1] +
               Ttop[2, 0] * V10fcs[2]),
        -1. * (Ttop[0, 1] * V10fcs[0] + Ttop[1, 1] * V10fcs[1] +
               Ttop[2, 1] * V10fcs[2]),
        -1. * (Ttop[0, 2] * V10fcs[0] + Ttop[1, 2] * V10fcs[1] +
               Ttop[2, 2] * V10fcs[2])
    ]
    Vt11bcs = [
        -1. * (Ttop[0, 0] * V11fcs[0] + Ttop[1, 0] * V11fcs[1] +
               Ttop[2, 0] * V11fcs[2]),
        -1. * (Ttop[0, 1] * V11fcs[0] + Ttop[1, 1] * V11fcs[1] +
               Ttop[2, 1] * V11fcs[2]),
        -1. * (Ttop[0, 2] * V11fcs[0] + Ttop[1, 2] * V11fcs[1] +
               Ttop[2, 2] * V11fcs[2])
    ]
    Vt12bcs = [
        -1. * (Ttop[0, 0] * V12fcs[0] + Ttop[1, 0] * V12fcs[1] +
               Ttop[2, 0] * V12fcs[2]),
        -1. * (Ttop[0, 1] * V12fcs[0] + Ttop[1, 1] * V12fcs[1] +
               Ttop[2, 1] * V12fcs[2]),
        -1. * (Ttop[0, 2] * V12fcs[0] + Ttop[1, 2] * V12fcs[1] +
               Ttop[2, 2] * V12fcs[2])
    ]
    # Bottom:
    Vb1bcs = [
        -1. * (Tbot[0, 0] * V1fcs[0] + Tbot[1, 0] * V1fcs[1] +
               Tbot[2, 0] * V1fcs[2]),
        -1. * (Tbot[0, 1] * V1fcs[0] + Tbot[1, 1] * V1fcs[1] +
               Tbot[2, 1] * V1fcs[2]), -1. *
        (Tbot[0, 2] * V1fcs[0] + Tbot[1, 2] * V1fcs[1] + Tbot[2, 2] * V1fcs[2])
    ]
    Vb2bcs = [
        -1. * (Tbot[0, 0] * V2fcs[0] + Tbot[1, 0] * V2fcs[1] +
               Tbot[2, 0] * V2fcs[2]),
        -1. * (Tbot[0, 1] * V2fcs[0] + Tbot[1, 1] * V2fcs[1] +
               Tbot[2, 1] * V2fcs[2]), -1. *
        (Tbot[0, 2] * V2fcs[0] + Tbot[1, 2] * V2fcs[1] + Tbot[2, 2] * V2fcs[2])
    ]
    Vb3bcs = [
        -1. * (Tbot[0, 0] * V3fcs[0] + Tbot[1, 0] * V3fcs[1] +
               Tbot[2, 0] * V3fcs[2]),
        -1. * (Tbot[0, 1] * V3fcs[0] + Tbot[1, 1] * V3fcs[1] +
               Tbot[2, 1] * V3fcs[2]), -1. *
        (Tbot[0, 2] * V3fcs[0] + Tbot[1, 2] * V3fcs[1] + Tbot[2, 2] * V3fcs[2])
    ]
    Vb4bcs = [
        -1. * (Tbot[0, 0] * V4fcs[0] + Tbot[1, 0] * V4fcs[1] +
               Tbot[2, 0] * V4fcs[2]),
        -1. * (Tbot[0, 1] * V4fcs[0] + Tbot[1, 1] * V4fcs[1] +
               Tbot[2, 1] * V4fcs[2]), -1. *
        (Tbot[0, 2] * V4fcs[0] + Tbot[1, 2] * V4fcs[1] + Tbot[2, 2] * V4fcs[2])
    ]
    Vb5bcs = [
        -1. * (Tbot[0, 0] * V5fcs[0] + Tbot[1, 0] * V5fcs[1] +
               Tbot[2, 0] * V5fcs[2]),
        -1. * (Tbot[0, 1] * V5fcs[0] + Tbot[1, 1] * V5fcs[1] +
               Tbot[2, 1] * V5fcs[2]), -1. *
        (Tbot[0, 2] * V5fcs[0] + Tbot[1, 2] * V5fcs[1] + Tbot[2, 2] * V5fcs[2])
    ]
    Vb6bcs = [
        -1. * (Tbot[0, 0] * V6fcs[0] + Tbot[1, 0] * V6fcs[1] +
               Tbot[2, 0] * V6fcs[2]),
        -1. * (Tbot[0, 1] * V6fcs[0] + Tbot[1, 1] * V6fcs[1] +
               Tbot[2, 1] * V6fcs[2]), -1. *
        (Tbot[0, 2] * V6fcs[0] + Tbot[1, 2] * V6fcs[1] + Tbot[2, 2] * V6fcs[2])
    ]
    Vb7bcs = [
        -1. * (Tbot[0, 0] * V7fcs[0] + Tbot[1, 0] * V7fcs[1] +
               Tbot[2, 0] * V7fcs[2]),
        -1. * (Tbot[0, 1] * V7fcs[0] + Tbot[1, 1] * V7fcs[1] +
               Tbot[2, 1] * V7fcs[2]), -1. *
        (Tbot[0, 2] * V7fcs[0] + Tbot[1, 2] * V7fcs[1] + Tbot[2, 2] * V7fcs[2])
    ]
    Vb8bcs = [
        -1. * (Tbot[0, 0] * V8fcs[0] + Tbot[1, 0] * V8fcs[1] +
               Tbot[2, 0] * V8fcs[2]),
        -1. * (Tbot[0, 1] * V8fcs[0] + Tbot[1, 1] * V8fcs[1] +
               Tbot[2, 1] * V8fcs[2]), -1. *
        (Tbot[0, 2] * V8fcs[0] + Tbot[1, 2] * V8fcs[1] + Tbot[2, 2] * V8fcs[2])
    ]
    Vb9bcs = [
        -1. * (Tbot[0, 0] * V9fcs[0] + Tbot[1, 0] * V9fcs[1] +
               Tbot[2, 0] * V9fcs[2]),
        -1. * (Tbot[0, 1] * V9fcs[0] + Tbot[1, 1] * V9fcs[1] +
               Tbot[2, 1] * V9fcs[2]), -1. *
        (Tbot[0, 2] * V9fcs[0] + Tbot[1, 2] * V9fcs[1] + Tbot[2, 2] * V9fcs[2])
    ]
    Vb10bcs = [
        -1. * (Tbot[0, 0] * V10fcs[0] + Tbot[1, 0] * V10fcs[1] +
               Tbot[2, 0] * V10fcs[2]),
        -1. * (Tbot[0, 1] * V10fcs[0] + Tbot[1, 1] * V10fcs[1] +
               Tbot[2, 1] * V10fcs[2]),
        -1. * (Tbot[0, 2] * V10fcs[0] + Tbot[1, 2] * V10fcs[1] +
               Tbot[2, 2] * V10fcs[2])
    ]
    Vb11bcs = [
        -1. * (Tbot[0, 0] * V11fcs[0] + Tbot[1, 0] * V11fcs[1] +
               Tbot[2, 0] * V11fcs[2]),
        -1. * (Tbot[0, 1] * V11fcs[0] + Tbot[1, 1] * V11fcs[1] +
               Tbot[2, 1] * V11fcs[2]),
        -1. * (Tbot[0, 2] * V11fcs[0] + Tbot[1, 2] * V11fcs[1] +
               Tbot[2, 2] * V11fcs[2])
    ]
    Vb12bcs = [
        -1. * (Tbot[0, 0] * V12fcs[0] + Tbot[1, 0] * V12fcs[1] +
               Tbot[2, 0] * V12fcs[2]),
        -1. * (Tbot[0, 1] * V12fcs[0] + Tbot[1, 1] * V12fcs[1] +
               Tbot[2, 1] * V12fcs[2]),
        -1. * (Tbot[0, 2] * V12fcs[0] + Tbot[1, 2] * V12fcs[1] +
               Tbot[2, 2] * V12fcs[2])
    ]

    fgm_vars = mms.fgm(
        trange=[time_double(trange[0]) - 600,
                time_double(trange[1]) + 600],
        probe=probe,
        data_rate='srvy')
    if fgm_vars is None:
        print(
            'Problem loading FGM vars for calculating FEEPS gyrophase angles')

    # interpolate the FGM var to the MEC var timestamps
    tinterpol('mms' + probe + '_fgm_b_bcs_srvy_l2_bvec',
              'mms' + probe + '_mec_r_sun_bcs',
              newname='mms' + probe + '_fgm_b_bcs_srvy_l2_bvec_int')

    B = get_data('mms' + probe + '_fgm_b_bcs_srvy_l2_bvec_int')

    # Now calculate gyrophase
    # Telescope vectors perp to B:
    Tperp = np.zeros((len(rsunbcs[:, 0]), 3, 24))

    # Gyrophase:
    phi = np.zeros((len(rsunbcs[:, 0]), 24))

    for i in range(len(rsunbcs[:, 0])):
        uB = B.y[i, :] / np.sqrt(B.y[i, 0]**2 + B.y[i, 1]**2 + B.y[i, 2]**2)
        # Sun vector perp to B:
        Sperp = np.cross(
            np.cross(uB, rsunbcs[i, :] / np.sqrt(np.nansum(rsunbcs[i, :]**2))),
            uB)
        # Dusk vector perp to B:
        Dperp = np.cross(
            np.cross(uB,
                     rduskbcs[i, :] / np.sqrt(np.nansum(rduskbcs[i, :]**2))),
            uB)
        Tperp[i, :, 0] = np.cross(np.cross(uB, Vt1bcs), uB)
        Tperp[i, :, 1] = np.cross(np.cross(uB, Vt2bcs), uB)
        Tperp[i, :, 2] = np.cross(np.cross(uB, Vt3bcs), uB)
        Tperp[i, :, 3] = np.cross(np.cross(uB, Vt4bcs), uB)
        Tperp[i, :, 4] = np.cross(np.cross(uB, Vt5bcs), uB)
        Tperp[i, :, 5] = np.cross(np.cross(uB, Vt6bcs), uB)
        Tperp[i, :, 6] = np.cross(np.cross(uB, Vt7bcs), uB)
        Tperp[i, :, 7] = np.cross(np.cross(uB, Vt8bcs), uB)
        Tperp[i, :, 8] = np.cross(np.cross(uB, Vt9bcs), uB)
        Tperp[i, :, 9] = np.cross(np.cross(uB, Vt10bcs), uB)
        Tperp[i, :, 10] = np.cross(np.cross(uB, Vt11bcs), uB)
        Tperp[i, :, 11] = np.cross(np.cross(uB, Vt12bcs), uB)
        Tperp[i, :, 12] = np.cross(np.cross(uB, Vb1bcs), uB)
        Tperp[i, :, 13] = np.cross(np.cross(uB, Vb2bcs), uB)
        Tperp[i, :, 14] = np.cross(np.cross(uB, Vb3bcs), uB)
        Tperp[i, :, 15] = np.cross(np.cross(uB, Vb4bcs), uB)
        Tperp[i, :, 16] = np.cross(np.cross(uB, Vb5bcs), uB)
        Tperp[i, :, 17] = np.cross(np.cross(uB, Vb6bcs), uB)
        Tperp[i, :, 18] = np.cross(np.cross(uB, Vb7bcs), uB)
        Tperp[i, :, 19] = np.cross(np.cross(uB, Vb8bcs), uB)
        Tperp[i, :, 20] = np.cross(np.cross(uB, Vb9bcs), uB)
        Tperp[i, :, 21] = np.cross(np.cross(uB, Vb10bcs), uB)
        Tperp[i, :, 22] = np.cross(np.cross(uB, Vb11bcs), uB)
        Tperp[i, :, 23] = np.cross(np.cross(uB, Vb12bcs), uB)

        for j in range(24):
            th1 = np.arccos(
                np.nansum(Tperp[i, :, j] * Sperp) /
                (np.sqrt(np.nansum(Tperp[i, :, j]**2)) *
                 np.sqrt(np.nansum(Sperp**2))))
            th2 = np.arccos(
                np.nansum(Tperp[i, :, j] * Dperp) /
                (np.sqrt(np.nansum(Tperp[i, :, j]**2)) *
                 np.sqrt(np.nansum(Dperp**2))))
            if th1 <= np.pi / 2.0 and th2 < np.pi / 2:
                phi[i, j] = 2 * np.pi - th1
            if th1 < np.pi / 2.0 and th2 >= np.pi / 2.0:
                phi[i, j] = th1
            if th1 > np.pi / 2.0 and th2 <= np.pi / 2.0:
                phi[i, j] = 270.0 * np.pi / 180.0 - th2
            if th1 >= np.pi / 2.0 and th2 > np.pi / 2.0:
                phi[i, j] = th1

    saved = store_data('mms' + probe + '_epd_feeps_' + data_rate +
                       '_gyrophase',
                       data={
                           'x': rsun.times,
                           'y': phi * 180. / np.pi
                       })
    if not saved:
        print('Problem saving gyrophase angles')
        return

    options('mms' + probe + '_epd_feeps_' + data_rate + '_gyrophase', 'yrange',
            [0, 360.0])

    # Gyrophase always returns on time stamps from MEC data, get those closest to FEEPS time stamps:
    eyes = mms_feeps_active_eyes(trange, probe, data_rate, datatype, level)
    sensor_types = ['top', 'bottom']

    feepst = get_data('mms' + probe + '_epd_feeps_' + data_rate + '_' + level +
                      '_' + datatype + '_spinsectnum')

    indt = np.zeros(len(feepst.times), dtype='int32')
    gpd = get_data('mms' + probe + '_epd_feeps_' + data_rate + '_gyrophase')

    for i in range(len(feepst.times)):
        indt[i] = np.argwhere(
            np.abs(gpd.times - feepst.times[i]) == np.min(
                np.abs(gpd.times - feepst.times[i]))).flatten()[0]

    # Gyrophase always returns all 24 FEEPS telescopes, downselect based on species:
    iT = np.array([
        np.array(eyes[sensor_types[0]]) - 1,
        np.array(eyes[sensor_types[0]]) + 11
    ]).flatten().tolist()
    gp_data = np.zeros((len(gpd.times[indt]), len(iT)))

    #return (iT, gp_data, gpd)
    for i in range(len(iT)):
        gp_data[:, i] = gpd.y[indt, iT[i]]

    saved = store_data('mms' + probe + '_epd_feeps_' + data_rate + '_' +
                       level + '_' + datatype + '_gyrophase',
                       data={
                           'x': gpd.times[indt],
                           'y': gp_data
                       })

    if saved:
        options(
            'mms' + probe + '_epd_feeps_' + data_rate + '_' + level + '_' +
            datatype + '_gyrophase', 'yrange', [0.0, 360.0])
        return 'mms' + probe + '_epd_feeps_' + data_rate + '_' + level + '_' + datatype + '_gyrophase'
Пример #5
0
def mms_feeps_gpd(
        trange=['2017-07-11/22:30', '2017-07-11/22:35'],
        probe='2',
        data_rate='brst',
        level='l2',
        datatype='electron',
        data_units='intensity',
        bin_size=15,  # deg
        energy=[50, 500]):
    """
    Calculate gyrophase distributions using data from the MMS Fly's Eye Energetic Particle Sensor (FEEPS)

    Parameters
    ----------
        probe: str
            probe #, e.g., '4' for MMS4

        data_units: str
            'intensity' 

        datatype: str
            'electron' or 'ion'

        data_rate: str
            instrument data rate, e.g., 'srvy' or 'brst'

        level: str
            data level

        suffix: str
            suffix of the loaded data

        energy: list of float
            energy range to include in the calculation

        bin_size: float
            size of the pitch angle bins

    Returns
    --------
        Tplot variable containing the gyrophase distribution

    Notes
    ------
        Based on IDL code by Drew Turner (10 Oct 2017): mms_feeps_gpd.pro
    """

    if isinstance(probe, int):
        probe = str(probe)

    feeps_data = pyspedas.mms.feeps(trange=trange,
                                    data_rate=data_rate,
                                    probe=probe,
                                    level=level)

    if len(feeps_data) == 0:
        print('Problem loading FEEPS data for this time range.')
        return

    # Account for angular response (finite field of view) of instruments
    # elec can use +/-21.4 deg on each pitch angle as average response angle; ions can start with +/-10 deg, but both need to be further refined
    if datatype == 'electron': dAngResp = 21.4  # [deg]
    if datatype == 'ion': dAngResp = 10.0  # [deg]

    bin_size = float(bin_size)
    n_bins = 360.0 / bin_size
    gyro_bins = 360. * np.arange(n_bins + 1) / n_bins
    gyro_centers = 360. * np.arange(n_bins) / n_bins + (gyro_bins[1] -
                                                        gyro_bins[0]) / 2.

    # get the gyrophase angles
    # calculate the gyro phase angles from the magnetic field data
    gyro_vars = mms_feeps_getgyrophase(trange=trange,
                                       probe=probe,
                                       data_rate=data_rate,
                                       level=level,
                                       datatype=datatype)
    gyro_data = get_data('mms' + str(probe) + '_epd_feeps_' + data_rate + '_' +
                         level + '_' + datatype + '_gyrophase')

    if gyro_data is None or gyro_vars is None:
        print('Problem calculating gyrophase angles.')
        return

    eyes = mms_feeps_active_eyes(trange, probe, data_rate, datatype, level)

    data_map = {}

    if data_rate == 'srvy':
        # From Allison Jaynes @ LASP: The 6,7,8 sensors (out of 12) are ions,
        # so in the pitch angle array, the 5,6,7 columns (counting from zero) will be the ion pitch angles.
        # for electrons:
        if datatype == 'electron':
            data_map['top-electron'] = eyes['top'] - 1
            data_map['bottom-electron'] = eyes['bottom'] - 1
        elif datatype == 'ion':
            data_map['top-ion'] = eyes['top'] - 1
            data_map['bottom-ion'] = eyes['bottom'] - 1
    elif data_rate == 'brst':
        # note: the following are indices of the top/bottom sensors in pa_data
        # they should be consistent with pa_dlimits.labels
        data_map['top-electron'] = [0, 1, 2, 3, 4, 5, 6, 7, 8]
        data_map['bottom-electron'] = [9, 10, 11, 12, 13, 14, 15, 16, 17]
        # and ions:
        data_map['top-ion'] = [0, 1, 2]
        data_map['bottom-ion'] = [3, 4, 5]

    sensor_types = ['top', 'bottom']

    # First, initialize arrays for flux (dflux) and pitch angles (dpa) compiled from all sensors:
    if datatype == 'electron':
        dflux = np.zeros(
            (len(gyro_data.times),
             len(data_map['top-electron']) + len(data_map['bottom-electron'])))
    elif datatype == 'ion':
        dflux = np.zeros(
            (len(gyro_data.times),
             len(data_map['top-ion']) + len(data_map['bottom-ion'])))

    dpa = np.zeros(dflux.shape)

    for sensor_type in sensor_types:
        pa_map = data_map[sensor_type + '-' + datatype]
        particle_idxs = np.array(eyes[sensor_type]) - 1

        for isen in range(len(particle_idxs)):  # loop through sensors
            # get the data
            var_name = 'mms' + str(
                probe
            ) + '_epd_feeps_' + data_rate + '_' + level + '_' + datatype + '_' + sensor_type + '_' + data_units + '_sensorid_' + str(
                particle_idxs[isen] + 1) + '_clean_sun_removed'
            data = get_data(var_name)
            if data is None:
                print('Data not found: ' + var_name)
                continue
            data.y[data.y == 0.0] = np.nan  # remove any 0s before averaging
            # Energy indices to use:
            indx = np.argwhere((data.v <= energy[1]) & (data.v >= energy[0]))
            if len(indx) == 0:
                print(
                    'Energy range selected is not covered by the detector for FEEPS '
                    + datatype + ' data')
                continue
            dflux[:, pa_map[isen]] = np.nanmean(data.y[:, indx],
                                                axis=1).flatten()
            dpa[:, pa_map[isen]] = gyro_data.y[:, pa_map[isen]].flatten()

    # we need to replace the 0.0s left in after populating dpa with NaNs; these
    # 0.0s are left in there because these points aren't covered by sensors loaded
    # for this datatype/data_rate
    dpa[dpa == 0.0] = np.nan

    gyro_flux = np.zeros((len(gyro_data.times), int(n_bins)))
    delta_gyro = (gyro_bins[1] - gyro_bins[0]) / 2.0

    # Now loop through PA bins and time, find the telescopes where there is data in those bins and average it up!
    for it in range(len(dpa[:, 0])):
        for ipa in range(int(n_bins)):
            ind = np.argwhere(
                (dpa[it, :] + dAngResp >= gyro_centers[ipa] - delta_gyro)
                & (dpa[it, :] - dAngResp < gyro_centers[ipa] + delta_gyro))
            if len(ind) > 0:
                gyro_flux[it, ipa] = np.nanmean(dflux[it, ind],
                                                axis=0).flatten()

    # fill any missed bins with NAN
    gyro_flux[gyro_flux == 0.0] = np.nan

    en_range_string = str(int(energy[0])) + '-' + str(int(energy[1])) + 'keV'

    new_name = 'mms' + str(
        probe
    ) + '_epd_feeps_' + data_rate + '_' + level + '_' + datatype + '_' + data_units + '_' + en_range_string + '_gpd'

    saved = store_data(new_name,
                       data={
                           'x': gyro_data.times,
                           'y': gyro_flux,
                           'v': gyro_centers
                       })

    if saved:
        options(new_name, 'spec', True)
        options(new_name, 'zlog', False)
        return new_name