Antenna Classification Daily Summary¶

by Josh Dillon last updated October 17, 2022

This notebook parses and summarizes the output of the file_calibration notebook to produce a report on per-antenna malfunctions on a daily basis.

Quick links:

• Summary of Per Antenna Issues¶

• Figure 1: Per File Overall Antenna Classification Summary¶

• Figure 2: Per Classifier Antenna Flagging Summary¶

• Figure 3: Array Visualization of Overall Daily Classification¶

In [1]:
import numpy as np
import pandas as pd
import glob
import os
import matplotlib.pyplot as plt
from hera_cal import io
from hera_qm import ant_class
from uvtools.plot import plot_antpos, plot_antclass
%matplotlib inline
from IPython.display import display, HTML

Settings¶

In [2]:
# Parse settings from environment
ANT_CLASS_FOLDER = os.environ.get("ANT_CLASS_FOLDER", "./")
SUM_FILE = os.environ.get("SUM_FILE", None)
OC_SKIP_OUTRIGGERS = os.environ.get("OC_SKIP_OUTRIGGERS", "TRUE").upper() == "TRUE"

for param in ['ANT_CLASS_FOLDER', 'SUM_FILE', 'OC_SKIP_OUTRIGGERS']:
    print(f"{param} = '{eval(param)}'")
ANT_CLASS_FOLDER = '/mnt/sn1/2459871'
SUM_FILE = '/mnt/sn1/2459871/zen.2459871.25287.sum.uvh5'
OC_SKIP_OUTRIGGERS = 'True'
In [3]:
if SUM_FILE is not None:
    from astropy.time import Time, TimeDelta
    utc = Time(float(SUM_FILE.split('zen.')[-1].split('.sum.uvh5')[0]), format='jd').datetime
    print(f'Date: {utc.month}-{utc.day}-{utc.year}')
Date: 10-18-2022
In [4]:
# set thresholds for fraction of the day
overall_thresh = .1
all_zero_thresh = .1
eo_zeros_thresh = .1
cross_pol_thresh = .1
bad_fem_thresh = .1
high_power_thresh = .1
low_power_thresh = .1
low_corr_thresh = .1
bad_slope_thresh = .5
excess_rfi_thresh = .1
chisq_thresh = .25

Load classifications and other metadata¶

In [5]:
# Load csvs
csv_files = sorted(glob.glob(os.path.join(ANT_CLASS_FOLDER, '*.ant_class.csv')))
jds = [float(f.split('/')[-1].split('zen.')[-1].split('.sum')[0]) for f in csv_files]
tables = [pd.read_csv(f) for f in csv_files]
table_cols = tables[0].columns[1::2]
class_cols = tables[0].columns[2::2]
print(f'Found {len(csv_files)} csv files starting with {csv_files[0]}')
Found 1862 csv files starting with /mnt/sn1/2459871/zen.2459871.25287.sum.ant_class.csv
In [6]:
# parse ant_strings
ap_strs = np.array(tables[0]['Antenna'])
ants = sorted(set(int(a[:-1]) for a in ap_strs))
translator = ''.maketrans('e', 'n') | ''.maketrans('n', 'e')
In [7]:
# get node numbers
node_dict = {ant: 'Unknown' for ant in ants}
try:
    from hera_mc import cm_hookup
    hookup = cm_hookup.get_hookup('default')
    for ant_name in hookup:
        ant = int("".join(filter(str.isdigit, ant_name)))
        if ant in node_dict:
            if hookup[ant_name].get_part_from_type('node')['E<ground'] is not None:
                node_dict[ant] = int(hookup[ant_name].get_part_from_type('node')['E<ground'][1:])
except:
    pass
nodes = sorted(set(node_dict.values()))

Figure out and summarize per-antenna issues¶

In [8]:
def print_issue_summary(bad_ant_strs, title, notes=''):
    '''Print report for list of bad antenna polarizations strings'''
    unique_bad_antnums = [int(ap[:-1]) for ap in bad_ant_strs]
    display(HTML(f'<h2>{title}: ({len(bad_ant_strs)} antpols across {len(set([ba[:-1] for ba in bad_ant_strs]))} antennas)</h2>'))
    if len(notes) > 0:
        display(HTML(f'<h4>{notes}</h4>'))
    for node in nodes:
        if np.any([node == node_dict[a] for a in unique_bad_antnums]):
            aps = [ap for ap in bad_ant_strs if node_dict[int(ap[:-1])] == node]
            whole_ants = [str(wa) for wa in set([int(ap[:-1]) for ap in aps if ap.translate(translator) in bad_ant_strs])]
            single_pols =  [ap for ap in aps if ap.translate(translator) not in bad_ant_strs]
            print(f'Node {node}:')
            print(f'\tAntpols ({len(aps)} total): {", ".join(aps)}')
            print(f'\tWhole Ants ({len(whole_ants)} total): {", ".join(whole_ants)}')
            print(f'\tSingle Pols ({len(single_pols)} total): {", ".join(single_pols)}')
In [9]:
# precompute various helpful quantities
all_slopes = np.vstack([t['Autocorr Slope'] for t in tables])
median_slope = np.median(all_slopes)
bad_slopes = np.vstack([t['Autocorr Slope Class'] for t in tables]) == 'bad'
suspect_slopes = np.vstack([t['Autocorr Slope Class'] for t in tables]) == 'suspect'
all_powers = np.vstack([t['Autocorr Power'] for t in tables])
median_power = np.median(all_powers)
bad_powers = np.vstack([t['Autocorr Power Class'] for t in tables]) == 'bad'
suspect_powers = np.vstack([t['Autocorr Power Class'] for t in tables]) == 'suspect'
bad_rfi = np.vstack([t['RFI in Autos Class'] for t in tables]) == 'bad'
In [10]:
# find all zeros
all_zeros_strs = ap_strs[np.mean(np.vstack([t['Dead? Class'] for t in tables]) == 'bad', axis=0) > all_zero_thresh]
In [11]:
# find even/odd zeros
eo_zeros_strs = ap_strs[np.mean(np.vstack([t['Even/Odd Zeros Class'] for t in tables]) == 'bad', axis=0) > eo_zeros_thresh]
eo_zeros_strs = [ap for ap in eo_zeros_strs if ap not in all_zeros_strs] 
In [12]:
# find cross-polarized antennas
cross_pol_strs = ap_strs[np.mean(np.vstack([t['Cross-Polarized Class'] for t in tables]) == 'bad', axis=0) > cross_pol_thresh]
In [13]:
# find FEM power issues
fem_bad_conditions = (bad_powers & bad_slopes & bad_rfi) | (suspect_powers & bad_slopes & bad_rfi) | (bad_powers & suspect_slopes & bad_rfi)
fem_off_strs = ap_strs[np.mean(fem_bad_conditions & (all_powers < median_power) & (all_slopes > median_slope), axis=0) > bad_fem_thresh]
In [14]:
# find high power issues
high_power_strs = ap_strs[np.mean(bad_powers & (all_powers > median_power), axis=0) > high_power_thresh]
In [15]:
# find other low power issues
low_power_strs = ap_strs[np.mean(bad_powers & (all_powers < median_power), axis=0) > low_power_thresh]
low_power_strs = [ap for ap in low_power_strs if ap not in all_zeros_strs and ap not in fem_off_strs] 
In [16]:
# find low correlation (but not low power)
low_corr_strs = ap_strs[np.mean(np.vstack([t['Low Correlation Class'] for t in tables]) == 'bad', axis=0) > low_corr_thresh]
low_corr_strs = [ap for ap in low_corr_strs if ap not in (set(low_power_strs) | set(all_zeros_strs) | set(fem_off_strs))] 
In [17]:
# find bad bandpasses
bad_bandpass_strs = ap_strs[np.mean(bad_slopes, axis=0) > bad_slope_thresh]
bad_bandpass_strs = [ap for ap in bad_bandpass_strs if ap not in (set(low_power_strs) | set(all_zeros_strs) | set(high_power_strs) | set(fem_off_strs))]
In [18]:
# find antennas with excess RFI
excess_rfi_strs = ap_strs[np.mean(np.vstack([t['RFI in Autos Class'] for t in tables]) == 'bad', axis=0) > excess_rfi_thresh]
excess_rfi_strs = [ap for ap in excess_rfi_strs if ap not in (set(low_power_strs) | set(all_zeros_strs) |  set(fem_off_strs) |
                                                              set(bad_bandpass_strs) | set(high_power_strs))] 
In [19]:
# find antennas with high redcal chi^2
chisq_strs = ap_strs[np.mean(np.vstack([t['Redcal chi^2 Class'] for t in tables]) == 'bad', axis=0) > chisq_thresh]
chisq_strs = [ap for ap in chisq_strs if ap not in (set(bad_bandpass_strs) | set(low_power_strs) | set(excess_rfi_strs) | 
                                                    set(all_zeros_strs) | set(high_power_strs) | set(fem_off_strs) | set(eo_zeros_strs))]
if OC_SKIP_OUTRIGGERS:
    chisq_strs = [ap for ap in chisq_strs if int(ap[:-1]) < 320]
In [20]:
# collect all results
to_print = [(all_zeros_strs, 'All-Zeros', 'These antennas have visibilities that are more than half zeros.'),
            (eo_zeros_strs, 'Excess Zeros in Either Even or Odd Spectra', 
             'These antennas are showing evidence of packet loss or X-engine failure.'),
            (cross_pol_strs, 'Cross-Polarized', 'These antennas have their east and north cables swapped.'),
            (fem_off_strs, 'Likely FEM Power Issue', 'These antennas have low power, anomolously high slopes, and extra channels identified as RFI.'),            
            (high_power_strs, 'High Power', 'These antennas have high median power.'),
            (low_power_strs, 'Other Low Power Issues', 'These antennas have low power, but are not all-zeros and not FEM off.'),
            (low_corr_strs, 'Low Correlation, But Not Low Power', 'These antennas are low correlation, but their autocorrelation power levels look OK.'),
            (bad_bandpass_strs, 'Bad Bandpasses, But Not Bad Power', 
             'These antennas have unusual bandpass slopes, but are not all-zeros, high power, low power, or FEM off.'),
            (excess_rfi_strs, 'Excess RFI', 'These antennas have excess strucutre (identified as possible RFI) in their bandpassed relative to the ' + \
             'median antenna, but not low or high power or a bad bandpass.'),
            (chisq_strs, 'Redcal chi^2', 'These antennas have been idenfied as not redundantly calibrating well, even after passing the above checks.')]
In [21]:
def print_high_level_summary():
    for tp in sorted(to_print, key=lambda x: len(x[0]), reverse=True):
        print(f'{len(tp[0])} antpols (on {len(set([ap[:-1] for ap in tp[0]]))} antennas) flagged for {tp[1]}.')
        
def print_all_issue_summaries():
    for tp in to_print:
        print_issue_summary(*tp)

Summary of Per-Antenna Issues¶

In [22]:
print_high_level_summary()
50 antpols (on 30 antennas) flagged for Low Correlation, But Not Low Power.
41 antpols (on 31 antennas) flagged for Likely FEM Power Issue.
40 antpols (on 24 antennas) flagged for Redcal chi^2.
23 antpols (on 19 antennas) flagged for Excess RFI.
21 antpols (on 17 antennas) flagged for Bad Bandpasses, But Not Bad Power.
18 antpols (on 9 antennas) flagged for All-Zeros.
12 antpols (on 8 antennas) flagged for High Power.
6 antpols (on 6 antennas) flagged for Other Low Power Issues.
0 antpols (on 0 antennas) flagged for Excess Zeros in Either Even or Odd Spectra.
0 antpols (on 0 antennas) flagged for Cross-Polarized.
In [23]:
print_all_issue_summaries()

All-Zeros: (18 antpols across 9 antennas)

These antennas have visibilities that are more than half zeros.

Node 13:
	Antpols (6 total): 140e, 140n, 141e, 141n, 142e, 142n
	Whole Ants (3 total): 140, 141, 142
	Single Pols (0 total): 
Node 15:
	Antpols (6 total): 147e, 147n, 148e, 148n, 149e, 149n
	Whole Ants (3 total): 147, 148, 149
	Single Pols (0 total): 
Node 16:
	Antpols (6 total): 152e, 152n, 153e, 153n, 154e, 154n
	Whole Ants (3 total): 152, 153, 154
	Single Pols (0 total): 

Excess Zeros in Either Even or Odd Spectra: (0 antpols across 0 antennas)

These antennas are showing evidence of packet loss or X-engine failure.

Cross-Polarized: (0 antpols across 0 antennas)

These antennas have their east and north cables swapped.

Likely FEM Power Issue: (41 antpols across 31 antennas)

These antennas have low power, anomolously high slopes, and extra channels identified as RFI.

Node 1:
	Antpols (2 total): 27e, 27n
	Whole Ants (1 total): 27
	Single Pols (0 total): 
Node 3:
	Antpols (2 total): 51e, 68n
	Whole Ants (0 total): 
	Single Pols (2 total): 51e, 68n
Node 4:
	Antpols (3 total): 54e, 54n, 55n
	Whole Ants (1 total): 54
	Single Pols (1 total): 55n
Node 5:
	Antpols (11 total): 43e, 46n, 58e, 58n, 60e, 60n, 73e, 73n, 74e, 74n, 75n
	Whole Ants (4 total): 73, 58, 60, 74
	Single Pols (3 total): 43e, 46n, 75n
Node 7:
	Antpols (2 total): 117e, 117n
	Whole Ants (1 total): 117
	Single Pols (0 total): 
Node 8:
	Antpols (5 total): 84n, 102n, 103e, 103n, 120n
	Whole Ants (1 total): 103
	Single Pols (3 total): 84n, 102n, 120n
Node 9:
	Antpols (1 total): 108e
	Whole Ants (0 total): 
	Single Pols (1 total): 108e
Node 10:
	Antpols (3 total): 109n, 110n, 111n
	Whole Ants (0 total): 
	Single Pols (3 total): 109n, 110n, 111n
Node 12:
	Antpols (5 total): 135n, 155e, 156e, 179e, 179n
	Whole Ants (1 total): 179
	Single Pols (3 total): 135n, 155e, 156e
Node 13:
	Antpols (1 total): 180n
	Whole Ants (0 total): 
	Single Pols (1 total): 180n
Node 14:
	Antpols (1 total): 184n
	Whole Ants (0 total): 
	Single Pols (1 total): 184n
Node 15:
	Antpols (3 total): 150e, 170e, 190n
	Whole Ants (0 total): 
	Single Pols (3 total): 150e, 170e, 190n
Node 18:
	Antpols (2 total): 203e, 203n
	Whole Ants (1 total): 203
	Single Pols (0 total): 

High Power: (12 antpols across 8 antennas)

These antennas have high median power.

Node 13:
	Antpols (1 total): 182n
	Whole Ants (0 total): 
	Single Pols (1 total): 182n
Node 16:
	Antpols (2 total): 192n, 193e
	Whole Ants (0 total): 
	Single Pols (2 total): 192n, 193e
Node 18:
	Antpols (9 total): 201e, 201n, 219e, 219n, 220e, 220n, 222e, 222n, 239n
	Whole Ants (4 total): 201, 219, 220, 222
	Single Pols (1 total): 239n

Other Low Power Issues: (6 antpols across 6 antennas)

These antennas have low power, but are not all-zeros and not FEM off.

Node 8:
	Antpols (1 total): 104n
	Whole Ants (0 total): 
	Single Pols (1 total): 104n
Node 13:
	Antpols (1 total): 183e
	Whole Ants (0 total): 
	Single Pols (1 total): 183e
Node 14:
	Antpols (4 total): 143e, 166n, 184e, 185e
	Whole Ants (0 total): 
	Single Pols (4 total): 143e, 166n, 184e, 185e

Low Correlation, But Not Low Power: (50 antpols across 30 antennas)

These antennas are low correlation, but their autocorrelation power levels look OK.

Node 1:
	Antpols (1 total): 28n
	Whole Ants (0 total): 
	Single Pols (1 total): 28n
Node 3:
	Antpols (1 total): 320n
	Whole Ants (0 total): 
	Single Pols (1 total): 320n
Node 6:
	Antpols (3 total): 34e, 47e, 63n
	Whole Ants (0 total): 
	Single Pols (3 total): 34e, 47e, 63n
Node 9:
	Antpols (23 total): 88e, 88n, 89e, 89n, 90e, 90n, 91e, 91n, 105e, 105n, 106e, 106n, 107e, 107n, 108n, 124e, 124n, 125e, 125n, 126e, 126n, 325e, 325n
	Whole Ants (11 total): 325, 105, 106, 107, 88, 89, 90, 91, 124, 125, 126
	Single Pols (1 total): 108n
Node 10:
	Antpols (1 total): 92n
	Whole Ants (0 total): 
	Single Pols (1 total): 92n
Node 14:
	Antpols (17 total): 143n, 144e, 144n, 145e, 145n, 163e, 163n, 164e, 164n, 165e, 165n, 166e, 185n, 186e, 186n, 187e, 187n
	Whole Ants (7 total): 163, 164, 165, 144, 145, 186, 187
	Single Pols (3 total): 143n, 166e, 185n
Node 16:
	Antpols (2 total): 173e, 173n
	Whole Ants (1 total): 173
	Single Pols (0 total): 
Node 18:
	Antpols (2 total): 200e, 200n
	Whole Ants (1 total): 200
	Single Pols (0 total): 

Bad Bandpasses, But Not Bad Power: (21 antpols across 17 antennas)

These antennas have unusual bandpass slopes, but are not all-zeros, high power, low power, or FEM off.

Node 1:
	Antpols (2 total): 28e, 28n
	Whole Ants (1 total): 28
	Single Pols (0 total): 
Node 2:
	Antpols (2 total): 32n, 323e
	Whole Ants (0 total): 
	Single Pols (2 total): 32n, 323e
Node 3:
	Antpols (1 total): 50n
	Whole Ants (0 total): 
	Single Pols (1 total): 50n
Node 4:
	Antpols (1 total): 57e
	Whole Ants (0 total): 
	Single Pols (1 total): 57e
Node 5:
	Antpols (1 total): 59n
	Whole Ants (0 total): 
	Single Pols (1 total): 59n
Node 6:
	Antpols (4 total): 22e, 77e, 77n, 78e
	Whole Ants (1 total): 77
	Single Pols (2 total): 22e, 78e
Node 10:
	Antpols (2 total): 92e, 92n
	Whole Ants (1 total): 92
	Single Pols (0 total): 
Node 13:
	Antpols (1 total): 161n
	Whole Ants (0 total): 
	Single Pols (1 total): 161n
Node 14:
	Antpols (2 total): 165e, 166e
	Whole Ants (0 total): 
	Single Pols (2 total): 165e, 166e
Node 15:
	Antpols (3 total): 167e, 167n, 190e
	Whole Ants (1 total): 167
	Single Pols (1 total): 190e
Node 16:
	Antpols (1 total): 151e
	Whole Ants (0 total): 
	Single Pols (1 total): 151e
Node 18:
	Antpols (1 total): 200n
	Whole Ants (0 total): 
	Single Pols (1 total): 200n

Excess RFI: (23 antpols across 19 antennas)

These antennas have excess strucutre (identified as possible RFI) in their bandpassed relative to the median antenna, but not low or high power or a bad bandpass.

Node 1:
	Antpols (3 total): 4e, 18e, 18n
	Whole Ants (1 total): 18
	Single Pols (1 total): 4e
Node 2:
	Antpols (2 total): 10e, 33n
	Whole Ants (0 total): 
	Single Pols (2 total): 10e, 33n
Node 3:
	Antpols (2 total): 51n, 320n
	Whole Ants (0 total): 
	Single Pols (2 total): 51n, 320n
Node 5:
	Antpols (2 total): 44e, 44n
	Whole Ants (1 total): 44
	Single Pols (0 total): 
Node 6:
	Antpols (3 total): 34e, 47e, 63n
	Whole Ants (0 total): 
	Single Pols (3 total): 34e, 47e, 63n
Node 7:
	Antpols (4 total): 98n, 99e, 99n, 119n
	Whole Ants (1 total): 99
	Single Pols (2 total): 98n, 119n
Node 12:
	Antpols (3 total): 158n, 329e, 333e
	Whole Ants (0 total): 
	Single Pols (3 total): 158n, 329e, 333e
Node 13:
	Antpols (1 total): 182e
	Whole Ants (0 total): 
	Single Pols (1 total): 182e
Node 16:
	Antpols (2 total): 173e, 173n
	Whole Ants (1 total): 173
	Single Pols (0 total): 
Node 18:
	Antpols (1 total): 200e
	Whole Ants (0 total): 
	Single Pols (1 total): 200e

Redcal chi^2: (40 antpols across 24 antennas)

These antennas have been idenfied as not redundantly calibrating well, even after passing the above checks.

Node 2:
	Antpols (1 total): 32e
	Whole Ants (0 total): 
	Single Pols (1 total): 32e
Node 5:
	Antpols (1 total): 59e
	Whole Ants (0 total): 
	Single Pols (1 total): 59e
Node 6:
	Antpols (1 total): 22n
	Whole Ants (0 total): 
	Single Pols (1 total): 22n
Node 9:
	Antpols (21 total): 88e, 88n, 89e, 89n, 90e, 90n, 91e, 91n, 105e, 105n, 106e, 106n, 107e, 107n, 108n, 124e, 124n, 125e, 125n, 126e, 126n
	Whole Ants (10 total): 105, 106, 107, 88, 89, 90, 91, 124, 125, 126
	Single Pols (1 total): 108n
Node 14:
	Antpols (15 total): 143n, 144e, 144n, 145e, 145n, 163e, 163n, 164e, 164n, 165n, 185n, 186e, 186n, 187e, 187n
	Whole Ants (6 total): 163, 164, 144, 145, 186, 187
	Single Pols (3 total): 143n, 165n, 185n
Node 15:
	Antpols (1 total): 169n
	Whole Ants (0 total): 
	Single Pols (1 total): 169n

Full-Day Visualizations¶

In [24]:
def classification_array(col):
    class_array = np.vstack([t[col] for t in tables])
    class_array[class_array == 'good'] = 1.7
    class_array[class_array == 'suspect'] = 1
    class_array[class_array == 'bad'] = 0
    return class_array.astype(float)
In [25]:
def classification_plot(col):
    class_array = classification_array(col)
    plt.figure(figsize=(12, len(ants) / 10), dpi=100)
    plt.imshow(class_array.T, aspect='auto', interpolation='none', cmap='RdYlGn', vmin=0, vmax=2,
               extent=[jds[0] - np.floor(jds[0]), jds[-1] - np.floor(jds[0]), len(ants), 0])
    plt.xlabel(f'JD - {int(jds[0])}')
    plt.yticks(ticks=np.arange(.5, len(ants)+.5), labels=[ant for ant in ants], fontsize=6)
    plt.ylabel('Antenna Number (East First, Then North)')
    plt.gca().tick_params(right=True, top=True, labelright=True, labeltop=True)
    plt.tight_layout()
    plt.title(f'{col}: Green is "good", Yellow is "suspect", Red is "bad"')

Figure 1: Per-File Overall Antenna Classification Summary¶

This "big green board" shows the overall (i.e. after redundant calibration) classification of antennas on a per-file basis. This is useful for looking at time-dependent effects across the array. While only antenna numbers are labeled, both polarizations are shown, first East then North going down, above and below the antenna's tick mark.

In [26]:
classification_plot('Antenna Class')
In [27]:
# compute flag fractions for all classifiers and antennas
frac_flagged = []
for col in class_cols[1:]:
    class_array = np.vstack([t[col] for t in tables])
    class_array[class_array == 'good'] = False
    class_array[class_array == 'suspect'] = False
    class_array[class_array == 'bad'] = True
    frac_flagged.append(np.sum(class_array, axis=0))
In [28]:
def plot_flag_frac_all_classifiers():
    ticks = []
    for i, col in enumerate(list(class_cols[1:])):
        ticks.append(f'{col} ({np.nanmean(np.array(frac_flagged).astype(float)[i]) / len(csv_files):.2%})')
    plt.figure(figsize=(8, len(ants) / 10), dpi=100)
    plt.imshow(np.array(frac_flagged).astype(float).T, aspect='auto', interpolation='none', cmap='viridis')
    plt.xticks(ticks=np.arange(len(list(class_cols[1:]))), labels=ticks, rotation=-45, ha='left')
    plt.yticks(ticks=np.arange(.5, len(ap_strs)+.5, 2), labels=[ant for ant in ants], fontsize=6)
    plt.ylabel('Antenna Number (East First, Then North)')
    plt.gca().tick_params(right=True, labelright=True,)
    ax2 = plt.gca().twiny()
    ax2.set_xticks(ticks=np.arange(len(list(class_cols[1:]))), labels=ticks, rotation=45, ha='left')
    plt.colorbar(ax=plt.gca(), label=f'Number of Files Flagged Out of {len(csv_files)}', aspect=50)
    plt.tight_layout()

Figure 2: Per-Classifier Antenna Flagging Summary¶

This plot shows the fraction of files flagged for each reason for each antenna. It's useful for seeing which problems are transitory and which ones are more common. Note that not all flags are independent and in particular redcal chi^2 takes an OR of other classifications as an input. Also note that only antenna numbers are labeled, both polarizations are shown, first East then North going down, above and below the antenna's tick mark.

In [29]:
plot_flag_frac_all_classifiers()
In [30]:
if SUM_FILE is not None:
    hd = io.HERAData(SUM_FILE)
    ap_tuples = [(int(ap[:-1]), {'e': 'Jee', 'n': 'Jnn'}[ap[-1]]) for ap in ap_strs]
    bad_bools = np.mean(classification_array('Antenna Class') == 0, axis=0) > overall_thresh
    bad_aps = [ap_tuples[i] for i in np.arange(len(ap_tuples))[bad_bools]]
    suspect_bools = np.mean(classification_array('Antenna Class') == 1, axis=0) > overall_thresh
    suspect_aps = [ap_tuples[i] for i in np.arange(len(ap_tuples))[suspect_bools] if ap_tuples[i] not in bad_aps]
    good_aps = [ap for ap in ap_tuples if ap not in bad_aps and ap not in suspect_aps]
    overall_class = ant_class.AntennaClassification(bad=bad_aps, suspect=suspect_aps, good=good_aps)
In [31]:
def array_class_plot():
    fig, axes = plt.subplots(1, 2, figsize=(14, 6), dpi=100, gridspec_kw={'width_ratios': [2, 1]})
    plot_antclass(hd.antpos, overall_class, ax=axes[0], ants=[ant for ant in hd.data_ants if ant < 320], legend=False, 
                  title=f'HERA Core: Overall Flagging Based on {overall_thresh:.1%} Daily Threshold')
    plot_antclass(hd.antpos, overall_class, ax=axes[1], ants=[ant for ant in hd.data_ants if ant >= 320], radius=50, title='Outriggers')

Figure 3: Array Visualization of Overall Daily Classification¶

Overall classification of antenna-polarizations shown on the array layout. If any antenna is marked bad for any reason more than the threshold (default 10%), it is marked bad here. Likewise, if any antenna is marked suspect for more than 10% of the night (but not bad), it's suspect here.

In [32]:
if SUM_FILE is not None: array_class_plot()
In [ ]: