Source code for experiment_prototype.scan_classes.scans

#!/usr/bin/python

"""
    scans
    ~~~~~
    This is the module containing the Scan class. The Scan class contains the
    ScanClassBase members, as well as a scanbound (to be implemented), a beamdir
    dictionary and scan_beams dictionary which specify beam direction angle and beam
    order in a scan, respectively, for individual slices that are to be combined in this
    scan. Beam direction information gets passed on to the AveragingPeriod.

    :copyright: 2018 SuperDARN Canada
    :author: Marci Detwiller
"""

import sys
from experiment_prototype.scan_classes.averaging_periods import AveragingPeriod
from experiment_prototype.scan_classes.scan_class_base import ScanClassBase
from experiment_prototype.experiment_exception import ExperimentException


[docs]class Scan(ScanClassBase): """ Set up the scans. A scan is made up of AveragingPeriods at defined beam directions, and some other metadata for the scan itself. **The unique members of the scan are (not a member of the scanclassbase):** scanbound A list of seconds past the minute for scans to align to. """ def __init__(self, scan_keys, scan_slice_dict, scan_interface, transmit_metadata): ScanClassBase.__init__(self, scan_keys, scan_slice_dict, scan_interface, transmit_metadata) # scan metadata - must be the same between all slices combined in scan. Metadata includes: self.scanbound = self.slice_dict[self.slice_ids[0]]['scanbound'] for slice_id in self.slice_ids: if self.slice_dict[slice_id]['scanbound'] != self.scanbound: errmsg = """Scan Boundary not the Same Between Slices {} and {} combined in Scan""".format(self.slice_ids[0], slice_id) raise ExperimentException(errmsg) # NOTE: for now we assume that when INTTIME combined, the AveragingPeriods of the various slices in the scan are # just interleaved 1 then the other. # Create a dictionary of beam directions for slice_id # self.beamdir = {} self.scan_beams = {} for slice_id in self.slice_ids: self.beamdir[slice_id] = self.slice_dict[slice_id]['beam_angle'] self.scan_beams[slice_id] = self.slice_dict[slice_id]['beam_order'] self.aveperiods = [] self.nested_slice_list = self.get_inttime_slice_ids() for params in self.prep_for_nested_scan_class(): self.aveperiods.append(AveragingPeriod(*params)) # AveragingPeriod will be in slice_id # order
[docs] def get_inttime_slice_ids(self): """ Return the slice_ids that are within the AveragingPeriods in this Scan instance. Take the interface keys inside this scan and return a list of lists where each inner list contains the slices that are in an averagingperiod that is inside this scan. ie. len(nested_slice_list) = # of averagingperiods in this scan, len(nested_slice_list[0]) = # of slices in the first averagingperiod, etc. :returns: the nested_slice_list which is used when creating the AveragingPeriods for this scan. """ intt_combos = [] for k, interface_value in self.interface.items(): if (interface_value == "PULSE" or interface_value == "INTEGRATION"): intt_combos.append(list(k)) # Inside the scan, we have a subset of the interface dictionary including all combinations # of slice_id that are included in this Scan instance. They could be interfaced INTTIME, # INTEGRATION, or PULSE. We want to remove all of the INTTIME combinations as we want to # eventually have a list of lists (combos) that is of length = # of INTTIMEs in the scan, # with all slices included in the inttimes inside the inner lists. # TODO make example and diagram combos = self.slice_combos_sorter(intt_combos, self.slice_ids) if __debug__: print("Inttime slice id list: {}".format(combos)) return combos
[docs] def prep_for_nested_scan_class(self): """ Override of base method to give more information about beamorder and beamdir. Beam order and beamdir are required for instantiation of the nested class AveragingPeriod so we need to extract this information as well to fill self.aveperiods. :returns: a list of lists of parameters that can be directly passed into the nested ScanClassBase type, AveragingPeriod. the params_list is of length = # of AveragingPeriods in this scan. """ # Get the basic parameters for a ScanClassBase type params_list = ScanClassBase.prep_for_nested_scan_class(self) # Add the beam order and beam direction information that is necessary for # AveragingPeriods specifically. for params, inttime_list in zip(params_list, self.nested_slice_list): # Make sure the number of inttimes (as determined by length of slice['scan'] # is the same for slices combined in the averaging period. self.nested_beamorder = {} self.nested_beamdir = {} for slice_id in inttime_list: if len(self.scan_beams[slice_id]) != len(self.scan_beams[inttime_list[0]]): errmsg = """Slice {} and {} are INTEGRATION or PULSE interfaced but do not have the same number of integrations in their scan""".format(self.slice_ids[0], slice_id) raise ExperimentException(errmsg) else: self.nested_beamorder[slice_id] = self.scan_beams[slice_id] self.nested_beamdir[slice_id] = self.beamdir[slice_id] params.append(self.nested_beamorder) params.append(self.nested_beamdir) return params_list