Source code for marvin.contrib.vacs.firefly

# !usr/bin/env python
# -*- coding: utf-8 -*-
#
# Licensed under a 3-clause BSD license.
#
# @Author: Brian Cherinka
# @Date:   2018-10-11 17:51:43
# @Last modified by:   Brian Cherinka
# @Last Modified time: 2018-11-29 17:23:15

from __future__ import print_function, division, absolute_import

import numpy as np
import marvin.tools
import matplotlib.pyplot as plt
from marvin import log, config
from .base import VACMixIn, VACTarget

[docs]class CallableDict(dict): """ Creates dictionary object with keys that can be called without using round brackets. Enables to execute functions inside the dictionary object implicitly. """ def __getitem__(self, key): val = super().__getitem__(key) if callable(val): return val() return val
[docs]class FIREFLYVAC(VACMixIn): """Provides access to the MaNGA-FIREFLY VAC. VAC name: FIREFLY URL: https://www.sdss.org/dr17/manga/manga-data/manga-firefly-value-added-catalog/ Description: Returns integrated and resolved stellar population parameters fitted by FIREFLY Authors: Justus Neumann, Jianhui Lian, Daniel Thomas, Claudia Maraston, and Lewis Hill """ # Required parameters name = 'firefly' description = 'Returns stellar population parameters fitted by FIREFLY' version = {'DR15': 'v1_1_2', 'DR16': 'v1_1_2','DR17':'v3_1_1', 'MPL-11': 'v3_1_1'} display_name = 'Firefly' url = 'https://www.sdss.org/dr17/manga/manga-data/manga-firefly-value-added-catalog/' # optional Marvin Tools to attach your vac to include = (marvin.tools.cube.Cube, marvin.tools.maps.Maps, marvin.tools.modelcube.ModelCube) # Required method
[docs] def set_summary_file(self, release): ''' Sets the path to the Firefly summary file ''' # define the variables to build a unique path to your VAC file # look up the MaNGA drpver from the release drpver, dapver = config.lookUpVersions(release) self.path_params = [] if release in ['DR17', 'MPL-11']: self.path_params.append({'ver': self.version[release], 'drpver': drpver, 'models':'miles'}) self.path_params.append({'ver': self.version[release], 'drpver': drpver, 'models':'mastar'}) self.summary_file = [self.get_path('mangaffly', path_params=self.path_params[i]) for i in [0,1]] # specify a special data container for the general VAC tools self.data_container = dict(zip(['miles', 'mastar'], self.summary_file)) else: self.path_params.append({'ver': self.version[release], 'drpver': drpver}) # get_path returns False if the files do not exist locally self.summary_file = self.get_path('mangaffly', path_params=self.path_params[0])
# Required method
[docs] def get_target(self, parent_object): ''' Accesses VAC data for a specific target from a Marvin Tool object ''' # get any parameters you need from the parent object plateifu = parent_object.plateifu imagesz = int(parent_object.header['NAXIS1']) if not isinstance(self.summary_file,list): # for data releases <DR17 # download the vac from the SAS if it does not already exist locally if not self.file_exists(self.summary_file): log.info('Warning: This file is ~6 GB. It may take awhile to download') self.summary_file = self.download_vac('mangaffly', path_params=self.path_params) # create container for more complex return data. ffly = FFlyTarget(plateifu, vacfile=self.summary_file,imagesz=imagesz,release=parent_object.release) return ffly # for DR17 return dictionary; vacfile is only downloaded when key is selected ffly = CallableDict({ "miles":lambda: self.prepare_container(plateifu, imagesz, 0, parent_object.release), "mastar":lambda: self.prepare_container(plateifu, imagesz, 1, parent_object.release) }) return ffly
[docs] def prepare_container(self,plateifu,imagesz,n,release): ''' Auxillary method for get_target(), used only for DR17. VAC access is transfered to this method to assure that vacfile is only downloaded when key is selected.''' if not self.file_exists(self.summary_file[n]): log.info('Warning: This file is ~6 GB. It may take awhile to download') self.summary_file[n] = self.download_vac('mangaffly', path_params=self.path_params[n]) container = FFlyTarget(plateifu, vacfile=self.summary_file[n], imagesz=imagesz,release=release) return container
[docs]class FFlyTarget(VACTarget): ''' A customized target class to also display Firefly 2-d maps This class handles data the Firefly summary file. Row data from the summary file for the given target is returned via the `data` property. Specific Firefly parameters are available via the `stellar_pops` and `stellar_gradients` methods, respectively. 2-d maps from the Firefly data can be produced via the `plot_map` method. TODO: Parameters: targetid (str): The plateifu or mangaid designation vacfile (str): The path of the VAC summary file imagesz (int): The original array shape of the target cube Attributes: data: The target row data from the main VAC file targetid (str): The target identifier ''' def __init__(self, targetid, vacfile, imagesz=None,release='DR17'): super(FFlyTarget, self).__init__(targetid, vacfile) self._image_sz = imagesz self._parameters = ['lw_age', 'mw_age', 'lw_z', 'mw_z'] # select the index of the targetid from the main VAC extension self._idx = self._get_data(ext='GALAXY_INFO')['plateifu'] == targetid self.release = release
[docs] def stellar_pops(self, parameter=None): ''' Returns the global stellar population properties Returns the global stellar population property within 1 Re for a given stellar population parameter. If no parameter specified, returns the entire row. Parameters: parameter (str): The stellar population parameter to retrieve. Can be one of ['lw_age', 'mw_age', 'lw_z', 'mw_z']. Returns: The data from the FIREFLY summary file for the target galaxy ''' if parameter: assert parameter in self._parameters, 'parameter must be one of {0}'.format( self._parameters ) if not self._indata: return "No FIREFLY result exists for {0}".format(self.targetid) if parameter: return self._get_data(ext='GLOBAL_PARAMETERS')[parameter + '_1re'][self._idx] else: return self._get_data(ext='GLOBAL_PARAMETERS')[self._idx]
[docs] def stellar_gradients(self, parameter=None): ''' Returns the gradient of stellar population properties Returns the gradient of the stellar population property for a given stellar population parameter. If no parameter specified, returns the entire row. Parameters: parameter (str): The stellar population parameter to retrieve. Can be one of ['lw_age', 'mw_age', 'lw_z', 'mw_z']. Returns: The data from the FIREFLY summary file for the target galaxy ''' if parameter: assert parameter in self._parameters, 'parameter must be one of {0}'.format( self._parameters ) if not self._indata: return "No FIREFLY result exists for {0}".format(self.targetid) if parameter: return self._get_data(ext='GRADIENT_PARAMETERS')[parameter + '_gradient'][self._idx] else: return self._get_data(ext='GRADIENT_PARAMETERS')[self._idx]
def _make_map(self, parameter=None): ''' Extract and create a 2d map ''' params = self._parameters + [ 'e(b_v)', 'stellar_mass', 'surface_mass_density', 'signal_noise', ] assert parameter in params, 'Parameter must be one of {0}'.format( params) # get the required arrays binid = self._get_data(ext='SPATIAL_BINID')[self._idx].squeeze(axis=0) bin1d = self._get_data(ext='SPATIAL_INFO')[self._idx, :, 0][0] try: prop = self._get_data(ext=parameter + '_voronoi')[self._idx, :, 0][0] except: prop = self._get_data(ext=parameter + '_voronoi')[self._idx, :][0] image_sz = self._image_sz # make a base map and reshape to a 1d array maps = (np.zeros((image_sz, image_sz)) - 99).reshape(image_sz * image_sz) # find relevant elements in bin1d (anything not -9999) propinds = np.where(bin1d >= -1) # find relevant elements in binid (anything not -9999) inds = np.where(binid >= -1) # select the relevant elements from binid tmp = binid[inds] # find non-zero elements from the relevant elements newinds = np.where(tmp > -1)[0] # map the bin1d indices back to the original binid ai = bin1d[propinds].argsort() p = ai[np.searchsorted(bin1d[propinds], tmp[newinds], sorter=ai)] # replace map elements with the relevant prop parameter maps[newinds] = prop[p] # reshape the map array from 1d back to 2d maps = maps.reshape(image_sz, image_sz) return maps
[docs] def plot_map(self, parameter=None, mask=None): ''' Plot map of stellar population properties Plots a 2d map of the specified FIREFLY stellar population parameter using Matplotlib. Optionally mask the data when plotting using Numpy's Masked Array. Default is to mask map values < -10. Parameters: parameter (str): The named of the VORONOI stellar pop. parameter mask (nd-array): A Numpy array of masked values to apply to the map Returns: The matplotlib axis image object ''' if not self._indata: return "No FIREFLY result exists for {0}".format(self.targetid) # create the 2d map maps = self._make_map(parameter=parameter) # Correcting a minor bug in the DR15/DR16 VAC file: all S/N values are shifted by -9999. Adding +9999 to correct. # (maps elements with no data are set to -99 in the _make_map method) if (parameter=='signal_noise') and (self.release!='DR17'): maps[maps!=-99]+=9999 # only show the spaxels with non-empty values mask = (maps < -10) if not mask else mask masked_array = np.ma.array(maps, mask=mask) # plot the masked map fig, ax = plt.subplots() axim = ax.imshow(masked_array, interpolation='nearest', cmap='RdYlBu_r', origin='lower') ax.set_xlabel('spaxel') ax.set_ylabel('spaxel') ax.set_title('Firefly {0}'.format(parameter.title())) # plot the colour bar cbar = fig.colorbar(axim, ax=ax, shrink=0.9) cbar.set_label(parameter.title(), fontsize=18, labelpad=20) cbar.ax.tick_params(labelsize=22) return axim
[docs] def list_parameters(self): ''' List the parameters available for plotting ''' params = self._parameters + [ 'e(b_v)', 'stellar_mass', 'surface_mass_density', 'signal_noise', ] return params