Source code for astromatic.swarp

#! /usr/bin/env python
#encoding:UTF-8
#
# Copyright (c) 2009-2012 IAA-CSIC  - All rights reserved. 
# Author: Jose M. Ibanez. 
# Instituto de Astrofisica de Andalucia, IAA-CSIC
#
# This file is part of PAPI (PANIC Pipeline)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

# ======================================================================
#
# SWARP module.
#
# Author: Jose Miguel Ibanez Mengual <jmiguel@iaa.es>
#
# Update 2011-5-17 : substituted popen2 with subprocess
# ======================================================================

"""
A wrapper for SWARP (Astromatic.net, E.Bertin).

This wrapper allows you to configure SWARP, run it and get back its outputs 
without the need of editing SWARP configuration files. by default, configuration 
files are created on-the-fly, and SWARP is run silently via python.

Tested on SWARP versions 2.17.x
"""

# ======================================================================


import sys
import os
import subprocess
import re
import copy
import fileinput


# PAPI
import papi.misc.utils

# ======================================================================

__version__ = "0.1 (2010-06-09)"

# ======================================================================

[docs]class SWARPException(Exception): pass
# ======================================================================
[docs]class SWARP(object): """ A wrapper class to transparently use SWARP. """ _SW_config = { #------------------------------Output------------------------------------------- "IMAGEOUT_NAME": {"comment": "Output filename", "value": "coadd.fits"}, "WEIGHTOUT_NAME": {"comment": "Output weight-map filename", "value": "coadd.weight.fits"}, "HEADER_ONLY": {"comment": "Only a header as an output file (Y/N)?", "value": "N"}, "HEADER_SUFFIX": {"comment": 'Filename extension for additional headers', "value": ".head"}, #------------------------------- Input Weights -------------------------------- "WEIGHT_TYPE": {"comment": "BACKGROUND,MAP_RMS,MAP_VARIANCE or MAP_WEIGHT", "value": "MAP_WEIGHT"}, "WEIGHT_SUFFIX": {"comment": "Suffix to use for weight-maps", "value": ".weight.fits"}, "WEIGHT_IMAGE": {"comment": "Weightmap filename if suffix not used (all or for each weight-map)", "value": ""}, "WEIGHT_THRESH": {"comment": "Bad pixel weight-threshold", "value": ""}, #------------------------------- Co-addition ---------------------------------- "COMBINE": {"comment": 'Combine resampled images (Y/N)?', "value": 'Y'}, "COMBINE_TYPE": {"comment": "MEDIAN,AVERAGE,MIN,MAX,WEIGHTED,CHI2 or SUM", "value": "MEDIAN"}, "BLANK_BADPIXELS": {"comment": "Set to 0 pixels having a weight of 0", "value": "N"}, #-------------------------------- Astrometry ---------------------------------- "CELESTIAL_TYPE": {"comment": "NATIVE, PIXEL, EQUATORIAL,GALACTIC,ECLIPTIC, or SUPERGALACTIC", "value": "NATIVE"}, "PROJECTION_TYPE": {"comment": "Any WCS projection code or NONE", "value": "TAN"}, "PROJECTION_ERR": {"comment": "Maximum projection error (in output pixels), or 0 for no approximation", "value": 0.001 }, "CENTER_TYPE": {"comment": "MANUAL, ALL or MOST", "value": "ALL"}, "CENTER": {"comment": "Coordinates of the image center", "value": "00:00:00.0, +00:00:00.0"}, "PIXELSCALE_TYPE": {"comment": "MANUAL,FIT,MIN,MAX or MEDIAN", "value": "MEDIAN"}, "PIXEL_SCALE": {"comment": "Pixel scale", "value": 0}, "IMAGE_SIZE": {"comment": "Image size (0 = AUTOMATIC)", "value": "N"}, #-------------------------------- Resampling ---------------------------------- "RESAMPLE": {"comment": "Resample input images (Y/N)?", "value": "Y"}, "RESAMPLE_DIR": {"comment": "Directory path for resampled images", "value": "."}, "RESAMPLE_SUFFIX": {"comment": "filename extension for resampled images", "value": ".resamp.fits"}, "RESAMPLING_TYPE": {"comment": "NEAREST,BILINEAR,LANCZOS2,LANCZOS3or LANCZOS4 (1 per axis)", "value": "LANCZOS3"}, "OVERSAMPLING": {"comment": "Oversampling in each dimension 0 = automatic)", "value": 0}, "INTERPOLATE": {"comment": "Interpolate bad input pixels (Y/N)? (all or for each image)", "value": "N"}, "FSCALASTRO_TYPE": {"comment": "NONE,FIXED, or VARIABLE", "value": "FIXED"}, "FSCALE_KEYWORD": {"comment": "FITS keyword for the multiplicative factor applied to each input image", "value": "FLXSCALE"}, "FSCALE_DEFAULT": {"comment": "Default FSCALE value if not in header", "value": 1.0}, "GAIN_KEYWORD": {"comment": "FITS keyword for effect. gain (e-/ADU)", "value": "GAIN"}, "GAIN_DEFAULT": {"comment": "Default gain if no FITS keyword found 0 = infinity (all or for each image)", "value": 0.0}, "SATLEV_KEYWORD": {"comment": "FITS keyword for saturation level (ADU)", "value": "SATURATE"}, "SATLEV_DEFAULT": {"comment": "Default saturation if no FITS keyword", "value": 50000.0}, #--------------------------- Background subtraction --------------------------- "SUBTRACT_BACK": {"comment": "Subtraction sky background (Y/N)? (all or for each image)", "value": "Y"}, "BACK_TYPE": {"comment": "AUTO or MANUAL (all or for each image)", "value": "AUTO"}, "BACK_DEFAULT": {"comment": "Default background value in MANUAL (all or for each image)", "value": 0.0}, "BACK_SIZE": {"comment": "Background mesh size (pixels) all or for each image)", "value": 128}, "BACK_FILTERSIZE": {"comment": "Background map filter range (meshes) (all or for each image)", "value": 3}, "BACK_FILTTHRESH": {"comment": "Threshold above which the background-map filter operates", "value": 0.0}, #------------------------------ Memory management ----------------------------- "VMEM_DIR": {"comment": "Directory path for swap files", "value": "."}, "VMEM_MAX": {"comment": "Maximum amount of virtual memory (MB)", "value": 2047}, "MEM_MAX": {"comment": "Maximum amount of usable RAM (MB)", "value": 128}, "COMBINE_BUFSIZE": {"comment": "RAM dedicated to co-addition(MB)", "value": 64}, #------------------------------ Miscellaneous --------------------------------- "DELETE_TMPFILES": {"comment": "Delete temporary resampled FITS files (Y/N)?", "value": "Y"}, "COPY_KEYWORDS": {"comment": 'List of FITS keywords to propagate from the input to the output headers', "value": "OBJECT,RA,DEC,OBJECT,PIXSCALE,ROT-RTA,INSTRUME,TELESCOPE"}, "WRITE_FILEINFO": {"comment": "Write information about each input file in the output image header?", "value": "N"}, "WRITE_XML": {"comment": 'Write XML file (Y/N)?', "value": "N"}, "XML_NAME": {"comment": "Filename for XML output", "value": "swarp.xml"}, "XSL_URL": {"comment": "Filename for XSL style-sheet", "value": "file:///usr/local/Terapix//share/swarp/swarp.xsl"}, "VERBOSE_TYPE": {"comment": "QUIET,NORMAL or FULL", "value": "NORMAL"}, "NNODES": {"comment": "Number of nodes (for clusters)", "value": 1}, "NODE_INDEX": {"comment": "Node index (for clusters)", "value": 0}, "NTHREADS": {"comment": "1 single thread", "value": 1}, # -- Extra-keys (will not be saved in the main configuration file "CONFIG_FILE": {"comment": '[Extra key] name of the main configuration file', "value": "SWARP.conf"} } # -- Special config. keys that should not go into the config. file. _SW_config_special_keys = ["CONFIG_FILE"] def __init__(self): """ SWARP class constructor. If a specific config_file is provided, it is used. """ self.config = ( dict([(k, copy.deepcopy(SWARP._SW_config[k]["value"]))\ for k in SWARP._SW_config.keys()])) # Extra config parameters that will be added/updated to the current # values of the config file self.ext_config = {} self.program = None self.version = None
[docs] def setup(self, path=None): """ Look for SWARP program ('swarp'). If a full path is provided, only this path is checked. Raise a SWARPException if it failed. Return program and version if it succeed. """ # -- Finding SWARP program and its version # first look for 'swarp' candidates = ['swarp'] if (path): candidates = [path] selected = None for candidate in candidates: try: p = subprocess.Popen (candidate, shell = True, bufsize = 0, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.STDOUT, close_fds = True) versionline = p.communicate()[0].decode() if versionline.find("SWarp") != -1: selected = candidate break except IOError: continue if not selected: raise SWARPException( """ Cannot find SWARP program. Check your PATH, or provide the SWARP program path in the constructor. """) _program = selected #print versionline _version_match = re.search("[Vv]ersion ([0-9\.])+", versionline) if not _version_match: raise SWARPException( "Cannot determine SWARP version.") _version = _version_match.group()[8:] if not _version: raise SWARPException( "Cannot determine SWARP version.") # print "Use " + self.program + " [" + self.version + "]" return _program, _version
[docs] def update_config(self): """ Update the configuration files according to the current in-memory SWARP configuration. """ # -- Write main configuration file main_f = open(self.config['CONFIG_FILE'], 'w') for key in self.config.keys(): if (key in SWARP._SW_config_special_keys): continue if key == "CENTER": # tuple instead of a single value value = " ".join(map(str, self.config[key])) else: value = str(self.config[key]) print >>main_f, ("%-16s %-16s # %s" % (key, value, SWARP._SW_config[key]['comment'])) main_f.close()
[docs] def run(self, file_list, updateconfig=True, clean=False, path=None): """ Description ----------- Run SWARP for a given list of files (fits files), and it can be one single file. Parameters ---------- updateconfig: bool If True (default), the configuration files will be updated before running SWARP. clean: bool If clean is True (default: False), configuration files (if any) will be deleted after SWARP terminates. path: str Path to the 'swarp' application (binary file) in the system. """ if updateconfig: self.update_config() # Try to find SWARP program # This will raise an exception if it failed self.program, self.version = self.setup(path) # check how many files in the input if isinstance(file_list,list): my_files = "" for file in file_list: my_files = my_files + " " + file else: my_files = file_list # a single file # Compound extra config command line args ext_args="" for key in self.ext_config.keys(): ext_args=ext_args + " -" + key+ " " + str(self.ext_config[key]) commandline = ( self.program + " -c " + self.config['CONFIG_FILE'] + " " + ext_args + " " + my_files) #print commandline #rcode = os.system(commandline) rcode = papi.misc.utils.runCmd(commandline) if rcode == 0: raise SWARPException( "SWARP command [%s] failed." % str(commandline)) if clean: self.clean()
[docs] def clean(self, config=True): """ Remove the generated SWARP files (if any). If config is True, remove generated configuration files. """ try: if (config): os.unlink(self.config['CONFIG_FILE']) except OSError: pass
# ====================================================================== if __name__ == "__main__": if len(sys.argv) < 2: print("Not enough input parameters") sys.exit() swarp = SWARP() # Using a specific config file (updateconfig=False) swarp.config['CONFIG_FILE']="/disk-a/caha/panic/DEVELOP/PIPELINE/PANIC/trunk/config_files/swarp.conf" files = [line.replace("\n", "") for line in fileinput.input(sys.argv[1])] swarp.run(files, updateconfig=False, clean=False) # Using and creating internal default config file (updateconfig=True) #swarp2 = SWARP() #cat_files = [line.replace( "\n", "") for line in fileinput.input(sys.argv[1])] #swarp2.run(cat_files, updateconfig=True, clean=False) sys.exit()