def test_finalise_ad_input(self): ad = astrodata.open(os.path.join(TESTDATAPATH, 'GSAOI', 'S20150110S0208_sourcesDetected.fits')) # Needed due to problem with lazy loading ad.nddata tlm = datetime.now() ret = gt.finalise_adinput(ad, 'ASSOCSKY', '_forSky') dt1 = datetime.strptime(ret.phu.get('ASSOCSKY'), '%Y-%m-%dT%H:%M:%S') dt2 = datetime.strptime(ret.phu.get('GEM-TLM'), '%Y-%m-%dT%H:%M:%S') assert ret.filename == 'S20150110S0208_forSky.fits' assert abs(dt1 - tlm).total_seconds() <= 1 assert abs(dt2 - tlm).total_seconds() <= 1
def associateSky(self, rc): """ This primitive determines which sky AstroData objects are associated with each science AstroData object and adds this information to a dictionary (in the form {science1:[sky1,sky2],science2:[sky2,sky3]}), where science1 and science2 are the science AstroData objects and sky1, sky2 and sky3 are the sky AstroDataRecord objects, which is then added to the reduction context. The input sky AstroData objects can be provided by the user using the parameter 'sky'. Otherwise, the science AstroData objects are found in the main stream (as normal) and the sky AstroData objects are found in the sky stream. :param adinput: input science AstroData objects :type adinput: Astrodata or Python list of AstroData :param sky: The input sky frame(s) to be subtracted from the input science frame(s). The input sky frame(s) can be a list of sky filenames, a sky filename, a list of AstroData objects or a single AstroData object. Note: If there are multiple input science frames and one input sky frame provided, then the same sky frame will be applied to all inputs; otherwise the number of input sky frames must match the number of input science frames. :type sky: string, Python list of string, AstroData or Python list of Astrodata """ # Instantiate the log log = logutils.get_logger(__name__) # Log the standard "starting primitive" debug message log.debug(gt.log_message("primitive", "associateSky", "starting")) # Define the keyword to be used for the time stamp for this primitive timestamp_key = self.timestamp_keys["associateSky"] # Determine the suffix for this primitive suffix = rc["suffix"] # This primitives requires at least one input science AstroData object # and at least one input sky AstroData object ad_science_list = rc.get_inputs_as_astrodata() if rc["sky"]: # Use the list of sky frames provided by the user ad_sky_list = [] for sky in rc["sky"]: if not isinstance(sky, AstroData): sky = AstroData(sky) # Create a list of sky AstroDataRecord objects ad_sky_list.append(sky) else: # The seperateSky primitive puts the sky AstroData objects in the # sky stream. The get_stream function returns a list of AstroData # objects when style="AD" ad_sky_list = rc.get_stream(stream="sky", style="AD") if not ad_science_list or not ad_sky_list: log.warning("Cannot associate sky frames, since at least one " "science AstroData object and one sky AstroData " "object are required for associateSky") # Add the science and sky AstroData objects to the output science # and sky AstroData object lists, respectively, without further # processing, after adding the appropriate time stamp to the PHU # and updating the filename. if ad_science_list: ad_science_output_list = gt.finalise_adinput( adinput=ad_science_list, timestamp_key=timestamp_key, suffix=suffix) if ad_sky_list: ad_sky_output_list = gt.finalise_adinput( adinput=ad_sky_list, timestamp_key=timestamp_key, suffix=suffix) else: # Initialize the dictionary that will contain the association # between the science AstroData objects and the sky AstroData # objects sky_dict = {} # Loop over each science AstroData object in the science list for ad_science in ad_science_list: # Determine the sky AstroData objects that are associated with # this science AstroData object. Initialize the list of sky # AstroDataRecord objects adr_sky_list = [] # Use the ORIGNAME of the science AstroData object as the key # of the dictionary origname = ad_science.phu_get_key_value("ORIGNAME") # If use_all is True, use all of the sky AstroData objects for # each science AstroData object if rc["use_all"]: log.stdinfo("Associating all available sky AstroData " "objects to %s" % ad_science.filename) # Set the list of sky AstroDataRecord objects for this # science AstroData object equal to the input list of sky # AstroDataRecord objects adr_sky_list = RCR.AstroDataRecord(ad_sky_list) # Update the dictionary with the list of sky # AstroDataRecord objects associated with this science # AstroData object sky_dict.update({origname: adr_sky_list}) else: # Get the datetime object of the science AstroData object # using the appropriate descriptor ad_science_datetime = ad_science.ut_datetime() # Loop over each sky AstroData object in the sky list for ad_sky in ad_sky_list: # Get the datetime object of the sky AstroData object # using the appropriate descriptor ad_sky_datetime = ad_sky.ut_datetime() # Create a timedelta object using the value of the # "time" parameter seconds = datetime.timedelta(seconds=rc["time"]) # Select only those sky AstroData objects observed # within "time" seconds of the science AstroData object if (abs(ad_science_datetime - ad_sky_datetime) < seconds): # Get the distance of the science and sky AstroData # objects using the x_offset and y_offset # descriptors ad_science_distance = math.sqrt( ad_science.x_offset()**2 + ad_science.y_offset()**2) ad_sky_distance = math.sqrt( ad_sky.x_offset()**2 + ad_sky.y_offset()**2) # Select only those sky AstroData objects that are # greater than "distance" arcsec away from the # science AstroData object if (abs(ad_science_distance - ad_sky_distance) > rc["distance"]): adr_sky_list.append( RCR.AstroDataRecord(ad_sky)) # Update the dictionary with the list of sky # AstroDataRecord objects associated with this science # AstroData object sky_dict.update({origname: adr_sky_list}) if not sky_dict[origname]: log.warning("No sky frames available for %s" % origname) else: log.stdinfo("The sky frames associated with %s are:" % origname) for adr_sky in sky_dict[origname]: log.stdinfo(" %s" % adr_sky.ad.filename) # Add the appropriate time stamp to the PHU and change the filename # of the science and sky AstroData objects ad_science_output_list = gt.finalise_adinput( adinput=ad_science_list, timestamp_key=timestamp_key, suffix=suffix) ad_sky_output_list = gt.finalise_adinput( adinput=ad_sky_list, timestamp_key=timestamp_key, suffix=suffix) # Add the association dictionary to the reduction context rc["sky_dict"] = sky_dict # Report the list of output sky AstroData objects to the sky stream in # the reduction context rc.report_output(ad_sky_output_list, stream="sky") # Report the list of output science AstroData objects to the reduction # context rc.report_output(ad_science_output_list) yield rc
def subtractSky(self, rc): """ This function will subtract the science extension of the input sky frames from the science extension of the input science frames. The variance and data quality extension will be updated, if they exist. :param adinput: input science AstroData objects :type adinput: Astrodata or Python list of AstroData :param sky: The input sky frame(s) to be subtracted from the input science frame(s). The input sky frame(s) can be a list of sky filenames, a sky filename, a list of AstroData objects or a single AstroData object. Note: If there are multiple input science frames and one input sky frame provided, then the same sky frame will be applied to all inputs; otherwise the number of input sky frames must match the number of input science frames. :type sky: string, Python list of string, AstroData or Python list of Astrodata """ # Instantiate the log log = logutils.get_logger(__name__) # Log the standard "starting primitive" debug message log.debug(gt.log_message("primitive", "subtractSky", "starting")) # Define the keyword to be used for the time stamp for this primitive timestamp_key = self.timestamp_keys["subtractSky"] # Initialize the list of output sky corrected AstroData objects ad_output_list = [] if rc["sky"]: # Use the list of sky frames provided by the user. Generate a # dictionary associating the input sky AstroData objects to the # input science AstroData objects. sky = rc["sky"] ad_science_list = rc.get_inputs_as_astrodata() for i, ad in enumerate(ad_science_list): origname = ad.phu_get_key_value("ORIGNAME") if len(sky) == 1: sky_dict.update({origname: sky[0]}) elif len(sky) == len(ad_science_list): sky_dict.update({origname: RCR.AstroDataRecord(sky[i])}) else: raise Errors.Error("Number of input sky frames do not " "match number of input science frames") else: # The stackSkyFrames primitive puts the dictionary containing the # information associating the stacked sky frames to the science # frames in the reduction context sky_dict = rc["stacked_sky_dict"] # Loop over each science AstroData object in the science list for ad_science in rc.get_inputs_as_astrodata(): # Check whether the subtractSky primitive has been run previously timestamp_key = self.timestamp_keys["subtractSky"] if ad_science.phu_get_key_value(timestamp_key): log.warning("No changes will be made to %s, since it has " "already been processed by subtractSky" % (ad_science.filename)) # Append the input AstroData object to the list of output # AstroData objects without further processing ad_output_list.append(ad_science) continue # Retrieve the sky AstroData object associated with the input # science AstroData object origname = ad_science.phu_get_key_value("ORIGNAME") if origname in sky_dict: ad_sky_for_correction = sky_dict[origname].ad # Subtract the sky from the input AstroData object log.stdinfo("Subtracting the sky (%s) from the science " "AstroData object %s" % (ad_sky_for_correction.filename, ad_science.filename)) ad_science.sub(ad_sky_for_correction) else: # There is no appropriate sky for the intput AstroData object log.warning("No changes will be made to %s, since no " "appropriate sky could be retrieved" % (ad_science.filename)) # Append the input AstroData object to the list of output # AstroData objects without further processing ad_output_list.append(ad_science) continue # Append the output AstroData object to the list of output # AstroData objects ad_output_list.append(ad_science) # Add the appropriate time stamp to the PHU and update the filename # of the science and sky AstroData objects ad_output_list = gt.finalise_adinput( adinput=ad_output_list, timestamp_key=timestamp_key, suffix=rc["suffix"]) # Report the list of output AstroData objects to the reduction # context rc.report_output(ad_output_list) yield rc
def separateSky(self, rc): # Instantiate the log log = logutils.get_logger(__name__) # Log the standard "starting primitive" debug message log.debug(gt.log_message("primitive", "separateSky", "starting")) # Define the keyword to be used for the time stamp for this primitive timestamp_key = self.timestamp_keys["separateSky"] # Determine the suffix for this primitive suffix = rc["suffix"] # This primitive requires at least two input AstroData objects ad_input_list = rc.get_inputs_as_astrodata() if len(ad_input_list) < 2: log.warning("Cannot separate sky frames, since at least two " "input AstroData objects are required for " "separateSky") # Add the input AstroData objects to the output science AstroData # object list without further processing, after adding the # appropriate time stamp to the PHU and updating the filename. if ad_input_list: ad_science_output_list = gt.finalise_adinput( adinput=ad_input_list, timestamp_key=timestamp_key, suffix=suffix) else: # Initialize the lists of science and sky AstroData objects ad_science_list = [] ad_sky_list = [] # Loop over each input AstroData object in the input list for ad in ad_input_list: # If the all_on_source parameter is equal to yes, all the input # on-source AstroData objects can be used as a sky frame if rc["all_on_source"]: ad_science_list.append(ad) adsky = deepcopy(ad) ad_sky_list.append(adsky) # If any of the input AstroData objects contain a "SKYFRAME" # keyword, that input can be used as a sky frame elif ad.phu_get_key_value("SKYFRAME"): log.fullinfo("%s can be used as a sky frame" % ad.filename) # Append the input AstroData object to the list of sky # AstroData objects ad_sky_list.append(ad) else: # Automatically determine the sky frames. For now, assume # everything else is science. # Append the input AstroData object to the list of science # AstroData objects ad_science_list.append(ad) log.stdinfo("Science frames:") for ad_science in ad_science_list: log.stdinfo(" %s" % ad_science.filename) log.stdinfo("Sky frames:") for ad_sky in ad_sky_list: log.stdinfo(" %s" % ad_sky.filename) # Add the appropriate time stamp to the PHU and update the filename # of the science and sky AstroData objects ad_science_output_list = gt.finalise_adinput( adinput=ad_science_list, timestamp_key=timestamp_key, suffix=suffix) ad_sky_output_list = gt.finalise_adinput( adinput=ad_sky_list, timestamp_key=timestamp_key, suffix=suffix) # Report the list of output sky AstroData objects to the sky stream in # the reduction context rc.report_output(ad_sky_output_list, stream="sky") # Report the list of output science AstroData objects to the reduction # context rc.report_output(ad_science_output_list) yield rc
def stackSkyFrames(self, rc): # Instantiate the log log = logutils.get_logger(__name__) # Log the standard "starting primitive" debug message log.debug(gt.log_message("primitive", "stackSkyFrames", "starting")) # Define the keyword to be used for the time stamp for this primitive timestamp_key = self.timestamp_keys["stackSkyFrames"] # Initialize the list of output science and stacked sky AstroData # objects ad_science_output_list = [] ad_sky_for_correction_output_list = [] # Initialize the dictionary that will contain the association between # the science AstroData objects and the stacked sky AstroData objects stacked_sky_dict = {} # The associateSky primitive puts the dictionary containing the # information associating the sky frames to the science frames in # the reduction context sky_dict = rc["sky_dict"] # Loop over each science AstroData object in the science list ad_science_list = rc.get_inputs_as_astrodata() for ad_science in ad_science_list: # Retrieve the list of sky AstroData objects associated with the # input science AstroData object origname = ad_science.phu_get_key_value("ORIGNAME") if origname in sky_dict: adr_sky_list = sky_dict[origname] if not adr_sky_list: # There are no associated sky AstroData objects for this # science AstroData object log.warning("No sky frames available for %s" % origname) continue # Generate a unique suffix for the stacked sky AstroData object if origname.endswith(".fits"): sky_suffix = "_for%s" % origname[:-5] else: sky_suffix = "_for%s" % origname if len(adr_sky_list) == 1: # There is only one associated sky AstroData object for # this science AstroData object, so there is no need to # call stackFrames. Update the dictionary with the single # sky AstroDataRecord object associated with this science # AstroData object ad_sky = deepcopy(adr_sky_list[0].ad) # Update the filename ad_sky.filename = gt.filename_updater( adinput=ad_sky, suffix=sky_suffix, strip=True) # Create the AstroDataRecord for this new AstroData Object adr_sky = RCR.AstroDataRecord(ad_sky) log.fullinfo("Only one sky frame available for %s: %s" % ( origname, adr_sky.ad.filename)) # Update the dictionary with the stacked sky # AstroDataRecord object associated with this science # AstroData object stacked_sky_dict.update({origname: adr_sky}) # Update the output stacked sky AstroData list to contain # the sky for correction ad_sky_for_correction_output_list.append(adr_sky.ad) else: # Initialize the list of sky AstroData objects to be # stacked ad_sky_to_stack_list = [] # Combine the list of sky AstroData objects log.stdinfo("Combining the following sky frames for %s" % origname) for adr_sky in adr_sky_list: log.stdinfo(" %s" % adr_sky.ad.filename) ad_sky_to_stack_list.append(adr_sky.ad) # Add the sky AstroData objects to the forStack stream rc.report_output(ad_sky_to_stack_list, stream="forStack") # Call stackFrames using the sky AstroData objects in the # forStack stream. The stacked sky AstroData objects will # be added back into the forStack stream. rc.run("showInputs(stream='forStack')") rc.run("stackFrames(stream='forStack', suffix='%s'," "operation='%s')" % (sky_suffix, rc["operation"])) rc.run("showInputs(stream='forStack')") # Get the stacked sky AstroData object from the forStack # stream and empty the forStack stream, in preparation for # creating the next stacked sky AstroData object adr_stacked_sky_list = rc.get_stream( stream="forStack", empty=True) # Add the sky to be used to correct this science AstroData # object to the list of output sky AstroData objects if len(adr_stacked_sky_list) == 1: adr_stacked_sky = adr_stacked_sky_list[0] # Add the appropriate time stamps to the PHU gt.mark_history(adinput=adr_stacked_sky.ad, keyword=timestamp_key) ad_sky_for_correction_output_list.append( adr_stacked_sky.ad) # Update the dictionary with the stacked sky # AstroDataRecord object associated with this science # AstroData object stacked_sky_dict.update({origname: adr_stacked_sky}) else: log.warning("Problem with stacking") # Add the appropriate time stamp to the PHU and update the filename of # the science AstroData objects ad_science_output_list = gt.finalise_adinput( adinput=ad_science_list, timestamp_key=timestamp_key, suffix=rc["suffix"]) # Add the association dictionary to the reduction context rc["stacked_sky_dict"] = stacked_sky_dict # Report the list of output stacked sky AstroData objects to the # forSkyCorrection stream in the reduction context rc.report_output( ad_sky_for_correction_output_list, stream="forSkyCorrection") rc.run("showInputs(stream='forSkyCorrection')") # Report the list of output science AstroData objects to the reduction # context rc.report_output(ad_science_output_list) yield rc