Source code for data_buttons.galex

# Ensure python3 compatibility
from __future__ import absolute_import, print_function, division

import fnmatch
import glob
import gzip
import os
import shutil
import sys

import astropy.units as u
import numpy as np
from astropy.io import fits
from astroquery.mast import Observations
from astroquery.ned import Ned
from MontagePy.main import mProject, mHdr

from . import tools


[docs]def galex_button( galaxies, filters="both", radius=None, filepath=None, download_data=True, create_mosaic=True, jy_conversion=True, verbose=False, ): """Create a GALEX mosaic, given a galaxy name. Using a galaxy name and radius, queries around that object, downloads available GALEX data and mosaics into a final product. Because GALEX images are in counts/s and the integrations may be different lengths, we convert back to a raw count, add the frames and convert back to counts/s at the end. This effectively weights the frame by exposure time. Args: galaxies (str or list): Names of galaxies to create mosaics for. Resolved by NED. filters (str, optional): One of 'FUV', 'NUV', or 'both'. Selects which GALEX filters to create a mosaic for. Defaults to 'both'. radius (astropy.units.Quantity, optional): Radius around the galaxy to search for observations. Defaults to None, where it will query Ned to get size. filepath (str, optional): Path to save the working and output files to. If not specified, saves to current working directory. download_data (bool, optional): If True, will download data from MAST. Defaults to True. create_mosaic (bool, optional): Switching this to True will mosaic data as appropriate. Defaults to True. jy_conversion (bool, optional): Convert the mosaicked file from raw units to Jy/pix. Defaults to True. verbose (bool, optional): Print out messages during the process. Useful mainly for debugging purposes or large images. Defaults to False. """ if isinstance(galaxies, str): galaxies = [galaxies] if filters == "both": filters = ["NUV", "FUV"] else: filters = [filters] if filepath is not None: os.chdir(filepath) if radius is not None: original_radius = radius.copy() else: original_radius = None steps = [] if download_data: steps.append(1) if create_mosaic: steps.append(2) if jy_conversion: steps.append(3) for galaxy in galaxies: if verbose: print('Beginning '+galaxy) if radius is None: try: size_query = Ned.get_table(galaxy,table='diameters') radius = 1.2*np.max(size_query['NED Major Axis'])/2*u.arcsec radius = radius.to(u.deg) except: raise Warning(galaxy+' not resolved by Ned, using 0.2deg radius.') radius = 0.2*u.degree if not os.path.exists(galaxy): os.mkdir(galaxy) obs_table = Observations.query_criteria(objectname=galaxy, radius=radius, obs_type='all', obs_collection='GALEX') # Ignore any calibration observations. obs_table = obs_table[obs_table['intentType'] == 'science'] for galex_filter in filters: if verbose: print('Beginning GALEX '+galex_filter) if 1 in steps: # Pull out available data, and download it query_results = np.where(obs_table["filters"] == galex_filter)[0] # If there isn't any GALEX coverage, just skip if len(query_results) == 0: print(galaxy + " missing!") continue # We only want to print out download messages if # verbose is True, so redirect otherwise. if not verbose: sys.stdout = open(os.devnull,'w') dataProductsByID = Observations.get_product_list( obs_table[query_results] ) Observations.download_products( dataProductsByID, download_dir="galex_temp/" + galaxy, mrp_only=True, ) # And set back to the original for printing. if not verbose: sys.stdout = sys.__stdout__ if not os.path.exists(galaxy + "/GALEX/"): os.mkdir(galaxy + "/GALEX/") if not os.path.exists(galaxy + "/GALEX/" + galex_filter): os.mkdir(galaxy + "/GALEX/" + galex_filter) if not os.path.exists(galaxy + "/GALEX/" + galex_filter+'/raw'): os.mkdir(galaxy + "/GALEX/" + galex_filter+'/raw') if not os.path.exists(galaxy + "/GALEX/" + galex_filter+'/data'): os.mkdir(galaxy + "/GALEX/" + galex_filter+'/data') if not os.path.exists(galaxy + "/GALEX/" + galex_filter+'/reprojected'): os.mkdir(galaxy + "/GALEX/" + galex_filter+'/reprojected') if not os.path.exists(galaxy + "/GALEX/" + galex_filter+'/weight'): os.mkdir(galaxy + "/GALEX/" + galex_filter+'/weight') if not os.path.exists(galaxy + "/GALEX/" + galex_filter+'/outputs'): os.mkdir(galaxy + "/GALEX/" + galex_filter+'/outputs') ext_name = {"NUV": "nd", "FUV": "fd"}[galex_filter] # Pull out the relevant filter files files (either *nd* # or *fd*), extract and move to base folder. matches = [] for root, _, filenames in os.walk("galex_temp/" + galaxy): for filename in fnmatch.filter( filenames, "*" + ext_name + "-int.fits.gz" ): matches.append(os.path.join(root, filename)) for match in matches: with gzip.open(match, "rb") as f_in: with open(match[:-3], "wb") as f_out: shutil.copyfileobj(f_in, f_out) filename = match[:-3].split('/') os.rename( match[:-3], galaxy + "/GALEX/" + galex_filter + "/raw/" + filename[-1], ) # Clean up any temporary files. shutil.rmtree("galex_temp/" + galaxy, ignore_errors=True) mHdr(galaxy, 2 * radius.value, 2 * radius.value, galaxy + "/GALEX/"+galex_filter+"/outputs/header.hdr", resolution=1.5, ) if 2 in steps: # Read in these files and set anything more than 35 # arcmin out to NaN. if verbose: print('Performing initial weighted reprojections') galex_files = glob.glob(galaxy + "/GALEX/" + galex_filter + "/raw/*") for galex_file in galex_files: hdu = fits.open(galex_file)[0] i = np.linspace( -hdu.data.shape[0] / 2, hdu.data.shape[0] / 2, hdu.data.shape[0] ) j = np.linspace( -hdu.data.shape[1] / 2, hdu.data.shape[1] / 2, hdu.data.shape[1] ) iv, jv = np.meshgrid(i, j) r = iv ** 2 + jv ** 2 hdu.data[r >= 1400 ** 2] = np.nan hdu.writeto(galex_file.replace('/raw/','/data/'), overwrite=True) # Also create a weight map (sqrt EXPTIME). exp_time = np.ones(hdu.data.shape)*hdu.header['EXPTIME']**0.5 exp_time[np.isnan(hdu.data) == True] = np.nan fits.writeto(galex_file.replace('/raw/','/weight/'), exp_time,hdu.header, overwrite=True) # And reproject each map separately using this weighting. mProject(galex_file.replace('/raw/','/data/'), galex_file.replace('/raw/','/reprojected/'), galaxy + "/GALEX/"+galex_filter+"/outputs/header.hdr", weight_file=galex_file.replace('/raw/','/weight/')) # And mosaic! # Montage uses its size as the length of the square, # since we want a radius use twice that. mHdr( galaxy, 2 * radius.value, 2 * radius.value, galaxy + "/GALEX/"+galex_filter+"/outputs/header.hdr", resolution=1.5, ) tools.mosaic( galaxy + "/GALEX/" + galex_filter+'/reprojected', header=galaxy+"/GALEX/"+galex_filter+"/outputs/header.hdr", verbose=verbose, reproject=False, haveAreas=True, ) os.rename( "mosaic/mosaic.fits", galaxy + "/GALEX/"+galex_filter+"/outputs/"+galaxy+'.fits' ) shutil.rmtree("mosaic/", ignore_errors=True) if 3 in steps: if verbose: print('Converting to Jy') # Convert to Jy. convert_to_jy(galaxy + "/GALEX/" + galex_filter +'/outputs/'+galaxy+".fits", galex_filter, hdu_out=galaxy + "/GALEX/"+galaxy+'_' + galex_filter + ".fits") if original_radius is None: radius = None else: radius = original_radius.copy()
[docs]def convert_to_jy(hdu_in,galex_filter,hdu_out=None): """Convert from GALEX counts/s to Jy/pixel. GALEX maps are provided in units of counts/s. These can be converted to more helpful units via the conversion factors given at https://asd.gsfc.nasa.gov/archive/galex/FAQ/counts_background.html. Args: hdu_in (str or astropy.io.fits.PrimaryHDU): File name of GALEX .fits file, or an Astropy PrimaryHDU instance (i.e. the result of ``fits.open(file)[0]``). galex_filter (str): Either 'NUV' or 'FUV'. hdu_out (str, optional): If not None, will save the converted HDU out with this filename. Defaults to None. Returns: astropy.io.fits.PrimaryHDU: The HDU in units of Jy/pix. """ if isinstance(hdu_in,str): hdu = fits.open(hdu_in)[0] else: hdu = hdu_in.copy() data = hdu.data.copy() header = hdu.header.copy() # Conversion factors here from Clark+ (2017) conversion_factor = {'FUV':1.076e-4, 'NUV':3.373e-5}[galex_filter] data *= conversion_factor header['BUNIT'] = 'Jy/pix' if hdu_out is not None: fits.writeto(hdu_out, data,header, overwrite=True) return fits.PrimaryHDU(data=data,header=header)