示例#1
0
 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