Example #1
0
    def overlay_skies(self, ax, diameter=None, return_figure=True, **kwargs):
        """ Overlay the sky fibers on a plot

        Parameters:
            ax (Axis):
                The matplotlib axis object
            diameter (float):
                The fiber diameter in arcsec
            return_figure (bool):
                If True, returns the figure axis object.  Default is True
            kwargs:
                Any keyword arguments accepted by Matplotlib EllipseCollection
        """

        if self.wcs is None:
            raise MarvinError('No WCS found.  Cannot overlay sky fibers.')

        # check for sky coordinates
        if self.bundle.skies is None:
            self.bundle.get_sky_coordinates()

        # check the diameter
        if diameter:
            assert isinstance(diameter,
                              (float, int)), 'diameter must be a number'
        diameter = (diameter or 2.0) / float(self.header['SCALE'])

        # get sky fiber pixel positions
        fiber_pix = self.wcs.wcs_world2pix(self.bundle.skies, 1)
        outside_range = ((fiber_pix < 0) |
                         (fiber_pix > self.data.size[0])).any()
        if outside_range:
            raise MarvinError(
                'Cannot overlay sky fibers.  Image is too small.  '
                'Please retrieve a bigger image cutout')

        # some matplotlib kwargs
        kwargs['edgecolor'] = kwargs.get('edgecolor', 'Orange')
        kwargs['facecolor'] = kwargs.get('facecolor', 'none')
        kwargs['linewidth'] = kwargs.get('linewidth', 0.7)

        # draw the sky fibers
        ec = EllipseCollection(diameter,
                               diameter,
                               0.0,
                               units='xy',
                               offsets=fiber_pix,
                               transOffset=ax.transData,
                               **kwargs)
        ax.add_collection(ec)

        # Add a larger circle to help identify the sky fiber locations in large images.
        if (self.data.size[0] > 1000) or (self.data.size[1] > 1000):
            ec = EllipseCollection(diameter * 5,
                                   diameter * 5,
                                   0.0,
                                   units='xy',
                                   offsets=fiber_pix,
                                   transOffset=ax.transData,
                                   **kwargs)
            ax.add_collection(ec)

        if return_figure:
            return ax
Example #2
0
    def _load_modelcube_from_db(self):
        """Initialises a model cube from the DB."""

        mdb = marvin.marvindb
        plate, ifu = self.plateifu.split('-')

        if not mdb.isdbconnected:
            raise MarvinError('No db connected')

        else:

            datadb = mdb.datadb
            dapdb = mdb.dapdb

            dm = datamodel[self.release]
            if dm.db_only:
                if self.bintype not in dm.db_only:
                    raise marvin.core.exceptions.MarvinError(
                        'Specified bintype {0} is not '
                        'available in the DB'.format(self.bintype.name))

            if self.data:
                assert isinstance(self.data, dapdb.ModelCube), \
                    'data is not an instance of marvindb.dapdb.ModelCube.'
            else:
                # Initial query for version
                version_query = mdb.session.query(dapdb.ModelCube).join(
                    dapdb.File, datadb.PipelineInfo,
                    datadb.PipelineVersion).filter(
                        datadb.PipelineVersion.version ==
                        self._dapver).from_self()

                # Query for model cube parameters
                db_modelcube = version_query.join(
                    dapdb.File, datadb.Cube, datadb.IFUDesign).filter(
                        datadb.Cube.plate == plate,
                        datadb.IFUDesign.name == str(ifu)).from_self().join(
                            dapdb.File, dapdb.FileType
                        ).filter(dapdb.FileType.value == 'LOGCUBE').join(
                            dapdb.Structure, dapdb.BinType).join(
                                dapdb.Template, dapdb.Structure.template_kin_pk
                                == dapdb.Template.pk).filter(
                                    dapdb.BinType.name == self.bintype.name,
                                    dapdb.Template.name ==
                                    self.template.name).all()

                if len(db_modelcube) > 1:
                    raise MarvinError('more than one ModelCube found for '
                                      'this combination of parameters.')

                elif len(db_modelcube) == 0:
                    raise MarvinError(
                        'no ModelCube found for this combination of parameters.'
                    )

                self.data = db_modelcube[0]

            self.header = self.data.file.primary_header
            self.wcs = WCS(self.data.file.cube.wcs.makeHeader())
            self._wavelength = np.array(
                self.data.file.cube.wavelength.wavelength, dtype=np.float)
            self._redcorr = np.array(self.data.redcorr[0].value,
                                     dtype=np.float)
            self._shape = self.data.file.cube.shape.shape

            self.plateifu = str(self.header['PLATEIFU'].strip())
            self.mangaid = str(self.header['MANGAID'].strip())
Example #3
0
    def __init__(self,
                 input=None,
                 filename=None,
                 mangaid=None,
                 plateifu=None,
                 mode=None,
                 data=None,
                 release=None,
                 drpall=None,
                 download=None,
                 nsa_source='auto',
                 bintype=None,
                 template=None,
                 template_kin=None):

        if template_kin is not None:
            warnings.warn(
                'template_kin is deprecated and will be removed in a future version.',
                DeprecationWarning)
            template = template_kin if template is None else template

        # _set_datamodel will replace these strings with datamodel objects.
        self.bintype = bintype
        self.template = template
        self.datamodel = None
        self._bitmasks = None

        MarvinToolsClass.__init__(self,
                                  input=input,
                                  filename=filename,
                                  mangaid=mangaid,
                                  plateifu=plateifu,
                                  mode=mode,
                                  data=data,
                                  release=release,
                                  drpall=drpall,
                                  download=download)

        NSAMixIn.__init__(self, nsa_source=nsa_source)

        # Checks that DAP is at least MPL-5
        MPL5 = distutils.version.StrictVersion('2.0.2')
        if self.filename is None and distutils.version.StrictVersion(
                self._dapver) < MPL5:
            raise MarvinError('ModelCube requires at least dapver=\'2.0.2\'')

        self.header = None
        self.wcs = None
        self._wavelength = None
        self._redcorr = None
        self._shape = None

        # Model extensions
        self._extension_data = {}
        self._binned_flux = None
        self._redcorr = None
        self._full_fit = None
        self._emline_fit = None
        self._stellarcont_fit = None

        if self.data_origin == 'file':
            self._load_modelcube_from_file()
        elif self.data_origin == 'db':
            self._load_modelcube_from_db()
        elif self.data_origin == 'api':
            self._load_modelcube_from_api()
        else:
            raise marvin.core.exceptions.MarvinError(
                'data_origin={0} is not valid'.format(self.data_origin))

        # Confirm that drpver and dapver match the ones from the header.
        marvin.tools.maps.Maps._check_versions(self)
Example #4
0
    def release(self, value):
        """Fails when trying to set the release after instatiation."""

        raise MarvinError(
            'the release cannot be changed once the object has been instantiated.'
        )
Example #5
0
def showImage(path=None,
              plateifu=None,
              release=None,
              return_image=True,
              show_image=True,
              mode=None):
    ''' Crudely and coarsely show a galaxy image that has been downloaded

    This utility function quickly allows you to display a PNG IFU image that is located in your
    local SAS or from the remote Utah SAS.  A PIL Image object is also returned which allows you to
    manipulate the image after the fact.  See :ref:`marvin-image-show` for example usage.

    Either the path or plateifu keyword is required.

    Parameters:
        path (str):
            A string filepath to a local IFU image
        plateifu (str):
            A plateifu designation used to look for the IFU image in your local SAS
        return_image (bool):
            If ``True``, returns the PIL Image object for image manipulation.  Default is ``True``.
        show_image (bool):
            If ``True``, shows the requested image that is opened internally
        mode ({'local', 'remote', 'auto'}):
            The load mode to use. See
            :doc:`Mode secision tree</mode_decision>`.
        release (str):
            The release version of the images to return

    Returns:
        image (PIL Image or None):
            If return_image is set, returns a PIL Image object to allow for image manipulation, else returns None.

    '''

    # check inputs
    release = release if release else marvin.config.release
    drpver, __ = marvin.config.lookUpVersions(release=release)
    args = [path, plateifu]
    assert any(args), 'A filepath or plateifu must be specified!'

    # check path
    if path:
        if type(path) == list and len(path) > 1:
            raise MarvinError(
                'showImage currently only works on a single input at a time')
        filepath = path[0] if type(path) == list else path

        # Deal with the mode
        if mode == 'local' and 'https://data.sdss.org' in filepath:
            raise MarvinError('Remote url path not allowed in local mode')
        elif mode == 'remote' and 'https://data.sdss.org' not in filepath:
            raise MarvinError('Local path not allowed in remote mode')
        elif mode == 'auto':
            if 'https://data.sdss.org' in filepath:
                mode = 'remote'
            else:
                mode = 'local'

    def _do_local_plateifu():
        full = http_access.full('mangaimage',
                                plate=plateid,
                                drpver=drpver,
                                ifu=ifu,
                                dir3d='*')
        filepath = http_access.expand('', full=full)
        if filepath:
            filepath = filepath[0]
            return filepath
        else:
            raise MarvinError(
                'Error: No files found locally to match plateifu {0}. '
                'Use one of the image utility functions to download them first or '
                'switch to remote mode'.format(plateifu))

    def _do_remote_plateifu():
        filepath = http_access.url('mangaimage',
                                   plate=plateid,
                                   drpver=drpver,
                                   ifu=ifu,
                                   dir3d='stack')
        return filepath

    # check plateifu
    if plateifu:
        plateid, ifu = plateifu.split('-')
        http_access = HttpAccess(verbose=False)
        if mode == 'local':
            filepath = _do_local_plateifu()
        elif mode == 'remote':
            filepath = _do_remote_plateifu()
        elif mode == 'auto':
            try:
                filepath = _do_local_plateifu()
                mode = 'local'
            except MarvinError as e:
                warnings.warn('Local mode failed.  Trying remote.',
                              MarvinUserWarning)
                filepath = _do_remote_plateifu()
                mode = 'remote'

    # check if filepath exists either locally or remotely
    if mode == 'local':
        if not filepath or not os.path.isfile(filepath):
            raise MarvinError(
                'Error: local filepath {0} does not exist. '.format(filepath))
        else:
            fileobj = filepath
    elif mode == 'remote':
        r = requests.get(filepath)
        if not r.ok:
            raise MarvinError(
                'Error: remote filepath {0} does not exist'.format(filepath))
        else:
            fileobj = stringio(r.content)

    # Open the image
    try:
        image = PIL.Image.open(fileobj)
    except IOError as e:
        print('Error: cannot open image')
        image = None
    else:
        image.filename = filepath

    if image and show_image:
        # show the image
        image.show()

    # return the PIL Image object
    if return_image:
        return image
    else:
        return None
Example #6
0
    def _get_spaxel_quantities(self, x, y):
        """Returns a dictionary of spaxel quantities."""

        modelcube_quantities = FuzzyDict({})

        if self.data_origin == 'db':

            session = marvin.marvindb.session
            dapdb = marvin.marvindb.dapdb

        if self.data_origin == 'file' or self.data_origin == 'db':

            _db_row = None

            for dm in self.datamodel:

                data = {'value': None, 'ivar': None, 'mask': None}

                for key in data:

                    if key == 'ivar' and not dm.has_ivar():
                        continue
                    if key == 'mask' and not dm.has_mask():
                        continue

                    if self.data_origin == 'file':

                        extname = dm.fits_extension(None if key ==
                                                    'value' else key)
                        data[key] = self.data[extname].data[:, y, x]

                    elif self.data_origin == 'db':

                        colname = dm.db_column(None if key == 'value' else key)

                        if not _db_row:
                            _db_row = session.query(dapdb.ModelSpaxel).filter(
                                dapdb.ModelSpaxel.modelcube_pk == self.data.pk,
                                dapdb.ModelSpaxel.x == x,
                                dapdb.ModelSpaxel.y == y).one()

                        data[key] = np.array(getattr(_db_row, colname))

                modelcube_quantities[dm.name] = Spectrum(
                    data['value'],
                    ivar=data['ivar'],
                    mask=data['mask'],
                    wavelength=self._wavelength,
                    unit=dm.unit)

        if self.data_origin == 'api':

            params = {'release': self._release}
            url = marvin.config.urlmap['api']['getModelCubeQuantitiesSpaxel'][
                'url']

            try:
                response = self._toolInteraction(
                    url.format(name=self.plateifu,
                               x=x,
                               y=y,
                               bintype=self.bintype.name,
                               template=self.template.name,
                               params=params))
            except Exception as ee:
                raise MarvinError(
                    'found a problem when checking if remote modelcube '
                    'exists: {0}'.format(str(ee)))

            data = response.getData()

            for dm in self.datamodel:

                modelcube_quantities[dm.name] = Spectrum(
                    data[dm.name]['value'],
                    ivar=data[dm.name]['ivar'],
                    mask=data[dm.name]['mask'],
                    wavelength=data['wavelength'],
                    unit=dm.unit)

        return modelcube_quantities
Example #7
0
 def wrapper(self, *args, **kwargs):
     if self.mode == 'local':
         raise MarvinError('{0} not available in local mode'.format(fxn.__name__))
     else:
         return fxn(self, *args, **kwargs)
Example #8
0
def getImagesByPlate(plateid,
                     download=False,
                     mode=None,
                     as_url=None,
                     verbose=None,
                     release=None):
    ''' Get all images belonging to a given plate ID

    Retrieve all images belonging to a given plate ID from either your local filesystem SAS
    or the Utah SAS.  Optionally can download the images by rsync using
    sdss_access.

    When as_url is False, both local and remote modes will allow you to access
    the full path to the images in your local SAS.  WHen as_url is True,
    local mode generates the Utah SAS url links, while remote mode generates the
    Utah SAS rsync links.

    Auto mode defaults to remote.

    Parameters:
        plateid (int):
            The plate ID to retrieve the images for.  Required.
        download (bool):
            Set to download the images from the SAS
        mode ({'local', 'remote', 'auto'}):
            The load mode to use. See
            :doc:`Mode secision tree</mode_decision>`.
            the cube exists.
        as_url (bool):
            Convert the list of images to use the SAS url (mode=local)
            or the SAS rsync url (mode=remote)
        verbose (bool):
            Turns on verbosity during rsync
        release (str):
            The release version of the images to return

    Returns:
        listofimages (list):
            The list of images

    '''

    assert str(plateid).isdigit(), 'Plateid must be a numeric integer value'

    # setup Rsync Access
    rsync_access = RsyncAccess(label='marvin_getplate', verbose=verbose)

    # setup marvin inputs
    release = release if release else marvin.config.release
    drpver, __ = marvin.config.lookUpVersions(release=release)
    dir3d = getDir3d(plateid, mode=mode, release=release)

    # if mode is auto, set it to remote:
    if mode == 'auto':
        warnings.warn(
            'Mode is auto.  Defaulting to remote.  If you want to access your '
            'local images, set the mode explicitly to local',
            MarvinUserWarning)
        mode = 'remote'

    # do a local or remote thing
    if mode == 'local':
        full = rsync_access.full('mangaimage',
                                 plate=plateid,
                                 drpver=drpver,
                                 ifu='*',
                                 dir3d=dir3d)
        listofimages = rsync_access.expand('', full=full, as_url=as_url)

        # if download, issue warning that cannot do it
        if download:
            warnings.warn('Download not available when in local mode',
                          MarvinUserWarning)

        return listofimages
    elif mode == 'remote':
        rsync_access.remote()
        rsync_access.add('mangaimage',
                         plate=plateid,
                         drpver=drpver,
                         ifu='*',
                         dir3d=dir3d)

        # set the stream
        try:
            rsync_access.set_stream()
        except AccessError as e:
            raise MarvinError(
                'Error with sdss_access rsync.set_stream. AccessError: {0}'.
                format(e))

        # get the list
        listofimages = rsync_access.get_urls(
        ) if as_url else rsync_access.get_paths()

        if download:
            rsync_access.commit()
        else:
            return listofimages
Example #9
0
    def _load_models(self):

        assert self.modelcube, 'a ModelCube is needed to initialise models.'

        if self.modelcube.data_origin == 'file':

            hdus = self.modelcube.data
            flux_array = hdus['FLUX'].data[:, self.y, self.x]
            flux_ivar = hdus['IVAR'].data[:, self.y, self.x]
            mask = hdus['MASK'].data[:, self.y, self.x]
            model_array = hdus['MODEL'].data[:, self.y, self.x]
            model_emline = hdus['EMLINE'].data[:, self.y, self.x]
            model_emline_base = hdus['EMLINE_BASE'].data[:, self.y, self.x]
            model_emline_mask = hdus['EMLINE_MASK'].data[:, self.y, self.x]

        elif self.modelcube.data_origin == 'db':

            if marvin.marvindb is None:
                raise MarvinError('there is not a valid DB connection.')

            session = marvin.marvindb.session
            dapdb = marvin.marvindb.dapdb

            modelcube_db_spaxel = session.query(dapdb.ModelSpaxel).filter(
                dapdb.ModelSpaxel.modelcube == self.modelcube.data,
                dapdb.ModelSpaxel.x == self.x,
                dapdb.ModelSpaxel.y == self.y).one()

            if modelcube_db_spaxel is None:
                raise MarvinError('cannot find a modelcube spaxel for '
                                  'x={0.x}, y={0.y}'.format(self))

            flux_array = modelcube_db_spaxel.flux
            flux_ivar = modelcube_db_spaxel.ivar
            mask = modelcube_db_spaxel.mask
            model_array = modelcube_db_spaxel.model
            model_emline = modelcube_db_spaxel.emline
            model_emline_base = modelcube_db_spaxel.emline_base
            model_emline_mask = modelcube_db_spaxel.emline_mask

        elif self.modelcube.data_origin == 'api':

            # Calls /modelcubes/<name>/models/<path:path> to retrieve a
            # dictionary with all the models for this spaxel.
            url = marvin.config.urlmap['api']['getModels']['url']
            url_full = url.format(name=self.plateifu,
                                  bintype=self.bintype,
                                  template_kin=self.template_kin,
                                  x=self.x,
                                  y=self.y)

            try:
                response = api.Interaction(url_full,
                                           params={'release': self._release})
            except Exception as ee:
                raise MarvinError(
                    'found a problem when checking if remote model cube '
                    'exists: {0}'.format(str(ee)))

            data = response.getData()

            flux_array = np.array(data['flux_array'])
            flux_ivar = np.array(data['flux_ivar'])
            mask = np.array(data['flux_mask'])
            model_array = np.array(data['model_array'])
            model_emline = np.array(data['model_emline'])
            model_emline_base = np.array(data['model_emline_base'])
            model_emline_mask = np.array(data['model_emline_mask'])

        # Instantiates the model attributes.

        self.redcorr = Spectrum(self.modelcube.redcorr,
                                wavelength=self.modelcube.wavelength,
                                wavelength_unit='Angstrom')

        self.model_flux = Spectrum(flux_array,
                                   units='1E-17 erg/s/cm^2/Ang/spaxel',
                                   wavelength=self.modelcube.wavelength,
                                   wavelength_unit='Angstrom',
                                   ivar=flux_ivar,
                                   mask=mask)

        self.model = Spectrum(model_array,
                              units='1E-17 erg/s/cm^2/Ang/spaxel',
                              wavelength=self.modelcube.wavelength,
                              wavelength_unit='Angstrom',
                              mask=mask)

        self.emline = Spectrum(model_emline,
                               units='1E-17 erg/s/cm^2/Ang/spaxel',
                               wavelength=self.modelcube.wavelength,
                               wavelength_unit='Angstrom',
                               mask=model_emline_mask)

        self.emline_base = Spectrum(model_emline_base,
                                    units='1E-17 erg/s/cm^2/Ang/spaxel',
                                    wavelength=self.modelcube.wavelength,
                                    wavelength_unit='Angstrom',
                                    mask=model_emline_mask)

        self.stellar_continuum = Spectrum(self.model.flux - self.emline.flux -
                                          self.emline_base.flux,
                                          units='1E-17 erg/s/cm^2/Ang/spaxel',
                                          wavelength=self.modelcube.wavelength,
                                          wavelength_unit='Angstrom',
                                          mask=model_emline_mask)
Example #10
0
    def sort(self, name, order='asc'):
        ''' Sort the set of results by column name

            Sorts the results by a given parameter / column name.  Sets
            the results to the new sorted results.

            Parameters:
                name (str):
                order ({'asc', 'desc'}):

            Returns:
                sortedres (list):
                    The listed of sorted results.

            Example:
                >>> r = q.run()
                >>> r.getColumns()
                >>> [u'mangaid', u'name', u'nsa.z']
                >>> r.results
                >>> [(u'4-3988', u'1901', -9999.0),
                >>>  (u'4-3862', u'1902', -9999.0),
                >>>  (u'4-3293', u'1901', -9999.0),
                >>>  (u'4-3602', u'1902', -9999.0),
                >>>  (u'4-4602', u'1901', -9999.0)]

                >>> # Sort the results by mangaid
                >>> r.sort('mangaid')
                >>> [(u'4-3293', u'1901', -9999.0),
                >>>  (u'4-3602', u'1902', -9999.0),
                >>>  (u'4-3862', u'1902', -9999.0),
                >>>  (u'4-3988', u'1901', -9999.0),
                >>>  (u'4-4602', u'1901', -9999.0)]

                >>> # Sort the results by IFU name in descending order
                >>> r.sort('ifu.name', order='desc')
                >>> [(u'4-3602', u'1902', -9999.0),
                >>>  (u'4-3862', u'1902', -9999.0),
                >>>  (u'4-3293', u'1901', -9999.0),
                >>>  (u'4-3988', u'1901', -9999.0),
                >>>  (u'4-4602', u'1901', -9999.0)]
        '''
        refname = self._getRefName(name)
        self.sortcol = refname
        self.order = order

        if self.mode == 'local':
            reverse = True if order == 'desc' else False
            sortedres = sorted(self.results, key=lambda row: row.__getattribute__(refname), reverse=reverse)
            self.results = sortedres
        elif self.mode == 'remote':
            # Fail if no route map initialized
            if not config.urlmap:
                raise MarvinError('No URL Map found.  Cannot make remote call')

            # Get the query route
            url = config.urlmap['api']['querycubes']['url']

            params = {'searchfilter': self.searchfilter, 'params': self.returnparams,
                      'sort': refname, 'order': order, 'limit': self.limit}
            try:
                ii = Interaction(route=url, params=params)
            except MarvinError as e:
                raise MarvinError('API Query Sort call failed: {0}'.format(e))
            else:
                self.results = ii.getData()
                self._makeNamedTuple()
                sortedres = self.results

        return sortedres
Example #11
0
    def _load_properties(self):
        """Initialises Spaxel.properties."""

        assert self.maps, 'a valid maps is needed to initialise the properties.'

        maps_properties = self.maps.properties

        if self.maps.data_origin == 'file':

            maps_hdu = self.maps.data

            properties = {}
            for prop in maps_properties:

                prop_hdu = maps_hdu[prop.name]
                prop_hdu_ivar = None if not prop.ivar else maps_hdu[prop.name +
                                                                    '_ivar']
                prop_hdu_mask = None if not prop.mask else maps_hdu[prop.name +
                                                                    '_mask']

                if prop.channels:
                    for ii, channel in enumerate(prop.channels):

                        if isinstance(prop.unit, str) or not prop.unit:
                            unit = prop.unit
                        else:
                            unit = prop.unit[ii]

                        properties[prop.fullname(
                            channel=channel)] = AnalysisProperty(
                                prop.name,
                                channel=channel,
                                value=prop_hdu.data[ii, self.y, self.x],
                                ivar=prop_hdu_ivar.data[ii, self.y, self.x]
                                if prop_hdu_ivar else None,
                                mask=prop_hdu_mask.data[ii, self.y, self.x]
                                if prop_hdu_mask else None,
                                unit=unit,
                                description=prop.description)

                else:

                    properties[prop.fullname(
                        channel=channel)] = AnalysisProperty(
                            prop.name,
                            channel=None,
                            value=prop_hdu.data[self.y, self.x],
                            ivar=prop_hdu_ivar.data[self.y, self.x]
                            if prop_hdu_ivar else None,
                            mask=prop_hdu_mask.data[self.y, self.x]
                            if prop_hdu_mask else None,
                            unit=prop.unit,
                            description=prop.description)

        elif self.maps.data_origin == 'db':

            if marvin.marvindb is None:
                raise MarvinError('there is not a valid DB connection.')

            session = marvin.marvindb.session
            dapdb = marvin.marvindb.dapdb

            # Gets the spaxel_index for this spaxel.
            spaxel_index = self.x * self.maps.shape[0] + self.y

            spaxelprops_table = dapdb.SpaxelProp if self._is_MPL4(
            ) else dapdb.SpaxelProp5
            spaxelprops = session.query(spaxelprops_table).filter(
                spaxelprops_table.file == self.maps.data,
                spaxelprops_table.spaxel_index == spaxel_index).one()

            if spaxelprops is None:
                raise MarvinError(
                    'cannot find a spaxelprops for x={0.x}, y={0.y}'.format(
                        self))

            properties = {}
            for prop in maps_properties:

                if prop.channels:

                    for ii, channel in enumerate(prop.channels):

                        if isinstance(prop.unit, str) or not prop.unit:
                            unit = prop.unit
                        else:
                            unit = prop.unit[ii]

                        properties[prop.fullname(
                            channel=channel)] = AnalysisProperty(
                                prop.name,
                                channel=channel,
                                value=getattr(spaxelprops,
                                              prop.fullname(channel=channel)),
                                ivar=(getattr(
                                    spaxelprops,
                                    prop.fullname(channel=channel, ext='ivar'))
                                      if prop.ivar else None),
                                mask=(getattr(
                                    spaxelprops,
                                    prop.fullname(channel=channel, ext='mask'))
                                      if prop.mask else None),
                                unit=unit,
                                description=prop.description)

                else:

                    properties[prop.fullname()] = AnalysisProperty(
                        prop.name,
                        channel=None,
                        value=getattr(spaxelprops, prop.fullname()),
                        ivar=(getattr(spaxelprops, prop.fullname(
                            ext='ivar')) if prop.ivar else None),
                        mask=(getattr(spaxelprops, prop.fullname(
                            ext='mask')) if prop.mask else None),
                        unit=prop.unit,
                        description=prop.description)

        elif self.maps.data_origin == 'api':

            # Calls /api/<name>/properties/<path:path> to retrieve a
            # dictionary with all the properties for this spaxel.
            routeparams = {
                'name': self.plateifu,
                'x': self.x,
                'y': self.y,
                'bintype': self.bintype,
                'template_kin': self.template_kin
            }

            url = marvin.config.urlmap['api']['getProperties']['url'].format(
                **routeparams)

            # Make the API call
            response = api.Interaction(url, params={'release': self._release})

            # Temporarily stores the arrays prior to subclassing from np.array
            data = response.getData()

            properties = {}
            for prop_fullname in data['properties']:
                prop = data['properties'][prop_fullname]
                properties[prop_fullname] = AnalysisProperty(
                    prop['name'],
                    channel=prop['channel'],
                    value=prop['value'],
                    ivar=prop['ivar'],
                    mask=prop['mask'],
                    unit=prop['unit'],
                    description=prop['description'])

        self.properties = DictOfProperties(properties)
Example #12
0
    def _load_spectrum(self):
        """Initialises Spaxel.spectrum."""

        assert self.cube, 'a valid cube is needed to initialise the spectrum.'

        if self.cube.data_origin == 'file':

            cube_hdu = self.cube.data

            self.spectrum = Spectrum(cube_hdu['FLUX'].data[:, self.y, self.x],
                                     units='1E-17 erg/s/cm^2/Ang/spaxel',
                                     wavelength=cube_hdu['WAVE'].data,
                                     wavelength_unit='Angstrom',
                                     ivar=cube_hdu['IVAR'].data[:, self.y,
                                                                self.x],
                                     mask=cube_hdu['MASK'].data[:, self.y,
                                                                self.x])

            self.specres = cube_hdu['SPECRES'].data
            self.specresd = cube_hdu['SPECRESD'].data

        elif self.cube.data_origin == 'db':

            if marvin.marvindb is None:
                raise MarvinError('there is not a valid DB connection.')

            session = marvin.marvindb.session
            datadb = marvin.marvindb.datadb

            cube_db = self.cube.data

            spaxel = session.query(datadb.Spaxel).filter(
                datadb.Spaxel.cube == cube_db, datadb.Spaxel.x == self.x,
                datadb.Spaxel.y == self.y).one()

            if spaxel is None:
                raise MarvinError(
                    'cannot find an spaxel for x={0.x}, y={0.y}'.format(self))

            self.spectrum = Spectrum(spaxel.flux,
                                     units='1E-17 erg/s/cm^2/Ang/spaxel',
                                     wavelength=cube_db.wavelength.wavelength,
                                     wavelength_unit='Angstrom',
                                     ivar=spaxel.ivar,
                                     mask=spaxel.mask)

            self.specres = np.array(cube_db.specres)
            self.specresd = None

        elif self.cube.data_origin == 'api':

            # Calls the API to retrieve the DRP spectrum information for this spaxel.

            routeparams = {'name': self.plateifu, 'x': self.x, 'y': self.y}

            url = marvin.config.urlmap['api']['getSpectrum']['url'].format(
                **routeparams)

            # Make the API call
            response = api.Interaction(url, params={'release': self._release})

            # Temporarily stores the arrays prior to subclassing from np.array
            data = response.getData()

            # Instantiates the spectrum from the returned values from the Interaction
            self.spectrum = Spectrum(data['flux'],
                                     units='1E-17 erg/s/cm^2/Ang/spaxel',
                                     wavelength=data['wavelength'],
                                     wavelength_unit='Angstrom',
                                     ivar=data['ivar'],
                                     mask=data['mask'])

            self.specres = np.array(data['specres'])
            self.specresd = None

            return response
Example #13
0
    def __init__(self, *args, **kwargs):

        valid_kwargs = [
            'x', 'y', 'cube_filename', 'maps_filename', 'modelcube_filename',
            'mangaid', 'plateifu', 'cube', 'maps', 'modelcube', 'bintype',
            'template_kin', 'template_pop', 'release', 'load', 'allow_binned'
        ]

        assert len(
            args) == 0, 'Spaxel does not accept arguments, only keywords.'
        for kw in kwargs:
            assert kw in valid_kwargs, 'keyword {0} is not valid'.format(kw)

        self.__allow_binned = kwargs.pop('allow_binned', False)

        self.cube = kwargs.pop('cube', True) or False
        self.maps = kwargs.pop('maps', True) or False
        self.modelcube = kwargs.pop('modelcube', True) or False

        if not self.cube and not self.maps and not self.modelcube:
            raise MarvinError(
                'either cube, maps, or modelcube must be True or '
                'a Marvin Cube, Maps, or ModelCube object must be specified.')

        # drop breadcrumb
        breadcrumb.drop(message='Initializing MarvinSpaxel {0}'.format(
            self.__class__),
                        category=self.__class__)

        # Checks versions
        input_release = kwargs.pop('release', marvin.config.release)
        self._release = self._check_version(input_release)

        self._drpver, self._dapver = marvin.config.lookUpVersions(
            release=self._release)

        self.plateifu = None
        self.mangaid = None

        if len(args) > 0:
            self.x = int(args[0])
            self.y = int(args[1])
        else:
            self.x = int(kwargs.pop('x', None))
            self.y = int(kwargs.pop('y', None))

        assert self.x is not None and self.y is not None, 'Spaxel requires x and y to initialise.'

        self.loaded = False

        self.specres = None
        self.specresd = None
        self.spectrum = None
        self.properties = {}

        self.model_flux = None
        self.redcorr = None
        self.model = None
        self.emline = None
        self.emline_base = None
        self.stellar_continuum = None
        self._parent_shape = None

        self.plateifu = kwargs.pop('plateifu', None)
        self.mangaid = kwargs.pop('mangaid', None)

        self.bintype = None
        self.template_kin = None

        if self.maps or self.modelcube:

            # Some versions, like DR13, don't have an associated DAP, so we check.
            assert self._dapver, 'this MPL/DR version does not have an associated dapver.'

            self.bintype = marvin.tools.maps._get_bintype(self._dapver,
                                                          bintype=kwargs.get(
                                                              'bintype', None))
            self.template_kin = marvin.tools.maps._get_template_kin(
                self._dapver, template_kin=kwargs.get('template_kin', None))

        self.__cube_filename = kwargs.pop('cube_filename', None)
        self.__maps_filename = kwargs.pop('maps_filename', None)
        self.__modelcube_filename = kwargs.pop('modelcube_filename', None)

        self._set_radec()

        if kwargs.pop('load', True):
            self.load()
Example #14
0
File: cube.py Project: zpace/marvin
def _getCube(name, use_file=False, release=None, **kwargs):
    ''' Retrieve a cube using marvin tools '''

    drpver, __ = config.lookUpVersions(release)

    cube = None
    results = {}

    # parse name into either mangaid or plateifu
    try:
        idtype = parseIdentifier(name)
    except Exception as ee:
        results['error'] = 'Failed to parse input name {0}: {1}'.format(
            name, str(ee))
        return cube, results

    filename = None
    plateifu = None
    mangaid = None

    try:
        if use_file:

            if idtype == 'mangaid':
                plate, ifu = mangaid2plateifu(name, drpver=drpver)
            elif idtype == 'plateifu':
                plate, ifu = name.split('-')

            if Path is not None:
                filename = Path().full('mangacube',
                                       ifu=ifu,
                                       plate=plate,
                                       drpver=drpver)
                assert os.path.exists(filename), 'file not found.'
            else:
                raise MarvinError('cannot create path for MaNGA cube.')

        else:

            if idtype == 'plateifu':
                plateifu = name
            elif idtype == 'mangaid':
                mangaid = name
            else:
                raise MarvinError(
                    'invalid plateifu or mangaid: {0}'.format(idtype))

        cube = Cube(filename=filename,
                    mangaid=mangaid,
                    plateifu=plateifu,
                    mode='local',
                    release=release)

        results['status'] = 1

    except Exception as ee:

        results['error'] = 'Failed to retrieve cube {0}: {1}'.format(
            name, str(ee))

    return cube, results
Example #15
0
    def getDictOf(self, name=None, format_type='listdict', to_json=False):
        ''' Get a dictionary of specified parameters

            Parameters:
                name (str):
                    Name of the parameter name to return.  If not specified,
                    it returns all parameters.
                format_type ({'listdict', 'dictlist'}):
                    The format of the results. Listdict is a list of dictionaries.
                    Dictlist is a dictionary of lists. Default is listdict.
                to_json (bool):
                    True/False boolean to convert the output into a JSON format

            Returns:
                output (list, dict):
                    Can be either a list of dictionaries, or a dictionary of lists

            Example:
                >>> # get some results
                >>> r = q.run()
                >>> # Get a list of dictionaries
                >>> r.getDictOf(format_type='listdict')
                >>> [{'cube.mangaid': u'4-3988', 'ifu.name': u'1901', 'nsa.z': -9999.0},
                >>>  {'cube.mangaid': u'4-3862', 'ifu.name': u'1902', 'nsa.z': -9999.0},
                >>>  {'cube.mangaid': u'4-3293', 'ifu.name': u'1901', 'nsa.z': -9999.0},
                >>>  {'cube.mangaid': u'4-3602', 'ifu.name': u'1902', 'nsa.z': -9999.0},
                >>>  {'cube.mangaid': u'4-4602', 'ifu.name': u'1901', 'nsa.z': -9999.0}]

                >>> # Get a dictionary of lists
                >>> r.getDictOf(format_type='dictlist')
                >>> {'cube.mangaid': [u'4-3988', u'4-3862', u'4-3293', u'4-3602', u'4-4602'],
                >>>  'ifu.name': [u'1901', u'1902', u'1901', u'1902', u'1901'],
                >>>  'nsa.z': [-9999.0, -9999.0, -9999.0, -9999.0, -9999.0]}

                >>> # Get a dictionary of only one parameter
                >>> r.getDictOf('mangaid')
                >>> [{'cube.mangaid': u'4-3988'},
                >>>  {'cube.mangaid': u'4-3862'},
                >>>  {'cube.mangaid': u'4-3293'},
                >>>  {'cube.mangaid': u'4-3602'},
                >>>  {'cube.mangaid': u'4-4602'}]

        '''

        # Try to get the sqlalchemy results keys
        keys = self.getColumns()
        tmp = self.mapColumnsToParams()
        tmp = self.mapParamsToColumns()

        if self.mode == 'local':
            lookup = OrderedDict(zip(keys, keys))
        elif self.mode == 'remote':
            lookup = self.coltoparam

        # Format results
        if format_type == 'listdict':
            output = [{lookup[k]: res.__getattribute__(k) for k in keys} for res in self.results]
        elif format_type == 'dictlist':
            output = {lookup[k]: [res.__getattribute__(k) for res in self.results] for k in keys}
        else:
            raise MarvinError('No output.  Check your input format_type.')

        # Test if name is in results
        if name:
            refname = self._getRefName(name)
            print('refname', refname)
            if refname:
                # Format results
                newname = lookup[refname]
                if format_type == 'listdict':
                    output = [{newname: i[newname]} for i in output]
                elif format_type == 'dictlist':
                    output = {newname: output[newname]}
                else:
                    output = None
            else:
                raise MarvinError('Name {0} not a property in results.  Try another'.format(name))

        if to_json:
            output = json.dumps(output) if output else None

        return output
Example #16
0
def getRandomImages(num=10,
                    download=False,
                    mode=None,
                    as_url=None,
                    verbose=None,
                    release=None):
    ''' Get a list of N random images from SAS

    Retrieve a random set of images from either your local filesystem SAS
    or the Utah SAS.  Optionally can download the images by rsync using
    sdss_access.

    When as_url is False, both local and remote modes will allow you to access
    the full path to the images in your local SAS.  WHen as_url is True,
    local mode generates the Utah SAS url links, while remote mode generates the
    Utah SAS rsync links.

    Auto mode defaults to remote.

    Parameters:
        num (int):
            The number of images to retrieve
        download (bool):
            Set to download the images from the SAS
        mode ({'local', 'remote', 'auto'}):
            The load mode to use. See
            :doc:`Mode secision tree</mode_decision>`.
            the cube exists.
        as_url (bool):
            Convert the list of images to use the SAS url (mode=local)
            or the SAS rsync url (mode=remote)
        verbose (bool):
            Turns on verbosity during rsync
        release (str):
            The release version of the images to return

    Returns:
        listofimages (list):
            The list of images

    '''
    release = release if release else marvin.config.release
    drpver, __ = marvin.config.lookUpVersions(release=release)
    rsync_access = RsyncAccess(label='marvin_getrandom', verbose=verbose)

    # if mode is auto, set it to remote:
    if mode == 'auto':
        warnings.warn(
            'Mode is auto.  Defaulting to remote.  If you want to access your '
            'local images, set the mode explicitly to local',
            MarvinUserWarning)
        mode = 'remote'

    # do a local or remote thing
    if mode == 'local':
        full = rsync_access.full('mangaimage',
                                 plate='*',
                                 drpver=drpver,
                                 ifu='*',
                                 dir3d='stack')
        listofimages = rsync_access.random('',
                                           full=full,
                                           num=num,
                                           refine='\d{4,5}.png',
                                           as_url=as_url)

        # if download, issue warning that cannot do it
        if download:
            warnings.warn('Download not available when in local mode',
                          MarvinUserWarning)

        return listofimages
    elif mode == 'remote':
        rsync_access.remote()
        rsync_access.add('mangaimage',
                         plate='*',
                         drpver=drpver,
                         ifu='*',
                         dir3d='stack')
        try:
            rsync_access.set_stream()
        except AccessError as e:
            raise MarvinError(
                'Error with sdss_access rsync.set_stream. AccessError: {0}'.
                format(e))

        # refine and randomize
        rsync_access.refine_task('\d{4,5}.png')
        rsync_access.shuffle()
        listofimages = rsync_access.get_urls(
            limit=num) if as_url else rsync_access.get_paths(limit=num)

        if download:
            rsync_access.commit()
        else:
            return listofimages
Example #17
0
    def getPrevious(self, chunk=None):
        ''' Retrieve the previous chunk of results.

            Returns a previous chunk of results from the query.
            from start to end in units of chunk.  Used with getNext
            to paginate through a long list of results

            Parameters:
                chunk (int):
                    The number of objects to return

            Returns:
                results (list):
                    A list of query results

            Example:
                >>> r = q.run()
                >>> r.getPrevious(5)
                >>> Retrieving previous 5, from 30 to 35
                >>> [(u'4-3988', u'1901', -9999.0),
                >>>  (u'4-3862', u'1902', -9999.0),
                >>>  (u'4-3293', u'1901', -9999.0),
                >>>  (u'4-3602', u'1902', -9999.0),
                >>>  (u'4-4602', u'1901', -9999.0)]

         '''

        newend = self.start
        self.chunk = chunk if chunk else self.chunk
        newstart = newend - self.chunk
        if newstart < 0:
            warnings.warn('You have reached the beginning.', MarvinUserWarning)
            newstart = 0
            newend = newstart + self.chunk

        log.info('Retrieving previous {0}, from {1} to {2}'.format(self.chunk, newstart, newend))
        if self.mode == 'local':
            self.results = self.query.slice(newstart, newend).all()
        elif self.mode == 'remote':
            # Fail if no route map initialized
            if not config.urlmap:
                raise MarvinError('No URL Map found.  Cannot make remote call')

            # Get the query route
            url = config.urlmap['api']['getsubset']['url']

            params = {'searchfilter': self.searchfilter, 'params': self.returnparams,
                      'start': newstart, 'end': newend, 'limit': self.limit,
                      'sort': self.sortcol, 'order': self.order}
            try:
                ii = Interaction(route=url, params=params)
            except MarvinError as e:
                raise MarvinError('API Query GetNext call failed: {0}'.format(e))
            else:
                self.results = ii.getData()
                self._makeNamedTuple()

        self.start = newstart
        self.end = newend

        if self.returntype:
            self.convertToTool()

        return self.results
Example #18
0
def getImagesByList(inputlist,
                    download=False,
                    mode=None,
                    as_url=None,
                    verbose=None,
                    release=None):
    ''' Get all images from a list of ids

    Retrieve a list of images from either your local filesystem SAS
    or the Utah SAS.  Optionally can download the images by rsync using
    sdss_access.

    When as_url is False, both local and remote modes will allow you to access
    the full path to the images in your local SAS.  WHen as_url is True,
    local mode generates the Utah SAS url links, while remote mode generates the
    Utah SAS rsync links.

    Auto mode defaults to remote.

    Parameters:
        inputlist (list):
            A list of plate-ifus or mangaids for the images you want to retrieve. Required.
        download (bool):
            Set to download the images from the SAS.  Only works in remote mode.
        mode ({'local', 'remote', 'auto'}):
            The load mode to use. See
            :doc:`Mode secision tree</mode_decision>`.
            the cube exists.
        as_url (bool):
            Convert the list of images to use the SAS url (mode=local)
            or the SAS rsync url (mode=remote)
        verbose (bool):
            Turns on verbosity during rsync
        release (str):
            The release version of the images to return

    Returns:
        listofimages (list):
            The list of images you have requested

    '''
    # Check inputs
    assert type(inputlist) == list or type(
        inputlist) == np.ndarray, 'Input must be of type list or Numpy array'
    idtype = parseIdentifier(inputlist[0])
    assert idtype in ['plateifu',
                      'mangaid'], 'Input must be of type plate-ifu or mangaid'
    # mode is checked via decorator

    # convert mangaids into plateifus
    if idtype == 'mangaid':
        newlist = []
        for myid in inputlist:
            try:
                plateifu = mangaid2plateifu(myid)
            except MarvinError as e:
                plateifu = None
            newlist.append(plateifu)
        inputlist = newlist

    # setup Rsync Access
    release = release if release else marvin.config.release
    drpver, __ = marvin.config.lookUpVersions(release=release)
    rsync_access = RsyncAccess(label='marvin_getlist', verbose=verbose)

    # if mode is auto, set it to remote:
    if mode == 'auto':
        warnings.warn(
            'Mode is auto.  Defaulting to remote.  If you want to access your '
            'local images, set the mode explicitly to local',
            MarvinUserWarning)
        mode = 'remote'

    # do a local or remote thing
    if mode == 'local':
        # Get list of images
        listofimages = []
        for plateifu in inputlist:
            dir3d = getDir3d(plateifu, mode=mode, release=release)
            plateid, ifu = plateifu.split('-')
            if as_url:
                path = rsync_access.url('mangaimage',
                                        plate=plateid,
                                        drpver=drpver,
                                        ifu=ifu,
                                        dir3d=dir3d)
            else:
                path = rsync_access.full('mangaimage',
                                         plate=plateid,
                                         drpver=drpver,
                                         ifu=ifu,
                                         dir3d=dir3d)
            listofimages.append(path)

        # if download, issue warning that cannot do it
        if download:
            warnings.warn('Download not available when in local mode',
                          MarvinUserWarning)

        return listofimages
    elif mode == 'remote':
        rsync_access.remote()
        # Add plateifus to stream
        for plateifu in inputlist:
            dir3d = getDir3d(plateifu, mode=mode, release=release)
            plateid, ifu = plateifu.split('-')
            rsync_access.add('mangaimage',
                             plate=plateid,
                             drpver=drpver,
                             ifu=ifu,
                             dir3d=dir3d)

        # set the stream
        try:
            rsync_access.set_stream()
        except AccessError as e:
            raise MarvinError(
                'Error with sdss_access rsync.set_stream. AccessError: {0}'.
                format(e))

        # get the list
        listofimages = rsync_access.get_urls(
        ) if as_url else rsync_access.get_paths()
        if download:
            rsync_access.commit()
        else:
            return listofimages
Example #19
0
File: rss.py Project: zpace/marvin
    def load(self):
        """Loads the fibre information."""

        assert self.loaded is False, 'object already loaded.'

        # Depending on whether the parent RSS is a file or API-populated, we
        # select the data to use.
        if self.rss.data_origin == 'file':

            # If the data origin is a file we use the HDUList in rss.data
            rss_data = self.rss.data

        elif self.rss.data_origin == 'api':

            # If data origin is the API, we make a request for the data
            # associated with this fiberid for all the extensions in the file.

            url = marvin.config.urlmap['api']['getRSSFiber']['url']

            try:
                response = self.rss._toolInteraction(url.format(name=self.rss.plateifu,
                                                                fiberid=self.fiberid))
            except Exception as ee:
                raise MarvinError('found a problem retrieving RSS fibre data for '
                                  'plateifu={!r}, fiberid={!r}: {}'.format(
                                      self.rss.plateifu, self.fiberid, str(ee)))

            api_data = response.getData()

            # Create a quick and dirty HDUList from the API data so that we
            # can parse it in the same way as if the data origin is file.
            rss_data = astropy.io.fits.HDUList([astropy.io.fits.PrimaryHDU()])
            for ext in api_data:
                rss_data.append(astropy.io.fits.ImageHDU(data=api_data[ext], name=ext.upper()))

        else:
            raise ValueError('invalid data_origin={!r}'.format(self.rss.data_origin))

        # Compile a list of all RSS datamodel extensions, either RSS or spectra
        datamodel_extensions = self.rss.datamodel.rss + self.rss.datamodel.spectra

        for extension in datamodel_extensions:

            # Retrieve the value (and mask and ivar, if associated) for each extension.
            value, ivar, mask = self._get_extension_data(extension, rss_data,
                                                         data_origin=self.rss.data_origin)

            if extension.name == 'flux':

                self.value[:] = value[:]
                self.ivar = ivar
                self.mask = mask
                self._set_unit(extension.unit)

            else:

                new_spectrum = Spectrum(value, self.wavelength, ivar=ivar, mask=mask,
                                        unit=extension.unit)
                setattr(self, extension.name, new_spectrum)

                self._spectra.append(extension.name)

        self.loaded = True
Example #20
0
 def wrapper(*args, **kwargs):
     if not RsyncAccess:
         raise MarvinError('sdss_access is not installed')
     else:
         return func(*args, **kwargs)
Example #21
0
def spaxel_factory(cls, *args, **kwargs):
    """A factory that returns the right type of spaxel depending on input.

    Based on the input values, determines if the resulting spaxels should be
    binned or unbinned, returning a `.Spaxel` or a `.Bin` respectively.
    This function is intended for overrding the ``__call__`` method in the
    `abc.ABCMeta` metacalass. The reason is that we want `.SpaxelBase` to have
    `abstract methods <abc.abstractmethod>` while also being a factory.
    See `this stack overflow <https://stackoverflow.com/a/5961102>`_ for
    details in the implementation of the ``__call__`` factory pattern.

    It can be used as::

        SpaxelABC = abc.ABCMeta
        SpaxelABC.__call__ = region_factory


        class SpaxelBase(object, metaclass=RegionABC):
            ...

    Note that this will override ``__call__`` everywhere else where
    `abc.ABCMeta` is used, but since it only overrides the default behaviour
    when the input class is `.SpaxelBase`, that should not be a problem.

    """

    Maps = marvin.tools.maps.Maps
    ModelCube = marvin.tools.modelcube.ModelCube

    if cls is not SpaxelBase:
        return type.__call__(cls, *args, **kwargs)

    cube = kwargs.pop('cube', None)
    maps = kwargs.pop('maps', None)
    modelcube = kwargs.pop('modelcube', None)

    plateifu = kwargs.pop(
        'plateifu',
        (getattr(cube, 'plateifu', None) or getattr(maps, 'plateifu', None)
         or getattr(modelcube, 'plateifu', None)))

    mangaid = kwargs.pop(
        'mangaid',
        (getattr(cube, 'mangaid', None) or getattr(maps, 'mangaid', None)
         or getattr(modelcube, 'mangaid', None)))

    release = kwargs.pop(
        'release',
        (getattr(cube, 'release', None) or getattr(maps, 'release', None)
         or getattr(modelcube, 'release', None)))

    spaxel_kwargs = kwargs.copy()
    spaxel_kwargs.update(cube=cube,
                         maps=maps,
                         modelcube=modelcube,
                         plateifu=plateifu,
                         mangaid=mangaid,
                         release=release)

    if not cube and not maps and not modelcube:
        raise MarvinError('no inputs defined.')

    if not maps and not modelcube:
        return Spaxel(*args, **spaxel_kwargs)

    if isinstance(maps, Maps) or isinstance(modelcube, ModelCube):
        bintype = getattr(maps, 'bintype', None) or getattr(
            modelcube, 'bintype', None)
        spaxel_kwargs.update(bintype=bintype)
        if bintype.binned:
            return Bin(*args, **spaxel_kwargs)
        else:
            return Spaxel(*args, **spaxel_kwargs)

    if maps:
        maps = Maps((maps if maps is not True else None) or plateifu
                    or mangaid,
                    release=release,
                    **kwargs)
        spaxel_kwargs.update(bintype=maps.bintype)
        if maps.bintype.binned:
            return Bin(*args, **spaxel_kwargs)

    if modelcube:
        modelcube = ModelCube((modelcube if modelcube is not True else None)
                              or plateifu or mangaid,
                              release=release,
                              **kwargs)
        spaxel_kwargs.update(bintype=modelcube.bintype)
        if modelcube.bintype.binned:
            return Bin(*args, **spaxel_kwargs)

    return Spaxel(*args, **spaxel_kwargs)

    raise MarvinError('you have reached the end of the SpaxelBase logic. '
                      'This should never happen!')
Example #22
0
    def get_binid(self, model=None):
        """Returns the 2D array for the binid map associated with ``model``."""

        assert model is None or isinstance(model, Model), 'invalid model type.'

        if model is not None:
            binid_prop = model.binid
        else:
            binid_prop = self.datamodel.parent.default_binid

        # Before MPL-6, the modelcube does not include the binid extension,
        # so we need to get the binid map from the associated MAPS.
        if (distutils.version.StrictVersion(self._dapver) <
                distutils.version.StrictVersion('2.1')):
            return self.getMaps().get_binid()

        if self.data_origin == 'file':

            if binid_prop.channel is None:
                return self.data[binid_prop.name].data[:, :]
            else:
                return self.data[binid_prop.name].data[
                    binid_prop.channel.idx, :, :]

        elif self.data_origin == 'db':

            mdb = marvin.marvindb

            table = mdb.dapdb.ModelSpaxel
            column = getattr(table, binid_prop.db_column())

            binid_list = mdb.session.query(column).filter(
                table.modelcube_pk == self.data.pk).order_by(table.x,
                                                             table.y).all()

            nx = ny = int(np.sqrt(len(binid_list)))
            binid_array = np.array(binid_list)

            return binid_array.transpose().reshape((ny, nx)).transpose(1, 0)

        elif self.data_origin == 'api':

            params = {'release': self._release}
            url = marvin.config.urlmap['api']['getModelCubeBinid']['url']

            extension = model.fits_extension().lower(
            ) if model is not None else 'flux'

            try:
                response = self._toolInteraction(url.format(
                    name=self.plateifu,
                    modelcube_extension=extension,
                    bintype=self.bintype.name,
                    template=self.template.name),
                                                 params=params)
            except Exception as ee:
                raise MarvinError('found a problem when checking if remote '
                                  'modelcube exists: {0}'.format(str(ee)))

            if response.results['error'] is not None:
                raise MarvinError(
                    'found a problem while getting the binid from API: {}'.
                    format(str(response.results['error'])))

            return np.array(response.getData()['binid'])
Example #23
0
def bpt_kewley06(maps, snr_min=3, return_figure=True, use_oi=True, **kwargs):
    """Returns a classification of ionisation regions, as defined in Kewley+06.

    Makes use of the classification system defined by
    `Kewley et al. (2006) <https://ui.adsabs.harvard.edu/#abs/2006MNRAS.372..961K/abstract>`_
    to return classification masks for different ionisation mechanisms. If ``return_figure=True``,
    produces and returns a matplotlib figure with the classification plots (based on
    Kewley+06 Fig. 4) and the 2D spatial distribution of classified spaxels (i.e., a map of the
    galaxy in which each spaxel is colour-coded based on its emission mechanism).

    While it is possible to call this function directly, its normal use will be via the
    :func:`~marvin.tools.maps.Maps.get_bpt` method.

    Parameters:
        maps (a Marvin :class:`~marvin.tools.maps.Maps` object)
            The Marvin Maps object that contains the emission line maps to be used to determine
            the BPT classification.
        snr_min (float or dict):
            The signal-to-noise cutoff value for the emission lines used to generate the BPT
            diagram. If ``snr_min`` is a single value, that signal-to-noise will be used for all
            the lines. Alternatively, a dictionary of signal-to-noise values, with the
            emission line channels as keys, can be used.
            E.g., ``snr_min={'ha': 5, 'nii': 3, 'oi': 1}``. If some values are not provided,
            they will default to ``SNR>=3``.
        return_figure (bool):
            If ``True``, it also returns the matplotlib figure_ of the BPT diagram plot,
            which can be used to modify the style of the plot.
        use_oi (bool):
            If ``True``, uses the OI diagnostic diagram for spaxel classification.

    Returns:
        bpt_return:
            ``bpt_kewley06`` returns a dictionary of dictionaries of classification masks.
            The classification masks (not to be confused with bitmasks) are boolean arrays with the
            same shape as the Maps or Cube (without the spectral dimension) that can be used
            to select spaxels belonging to a certain excitation process (e.g., star forming).
            The returned dictionary has the following keys: ``'sf'`` (star forming), ``'comp'``
            (composite), ``'agn'``, ``'seyfert'``, ``'liner'``, ``'invalid'``
            (spaxels that are masked out at the DAP level), and ``'ambiguous'`` (good spaxels that
            do not fall in any  classification or fall in more than one). Each key provides access
            to a new dictionary with keys ``'nii'`` (for the constraints in the diagram NII/Halpha
            vs OIII/Hbeta),  ``'sii'`` (SII/Halpha vs OIII/Hbeta), ``'oi'`` (OI/Halpha vs
            OIII/Hbeta; only if ``use_oi=True``), and ``'global'``, which applies all the previous
            constraints at once. The ``'ambiguous'`` mask only contains the ``'global'``
            subclassification, while the ``'comp'`` dictionary only contains ``'nii'``.
            ``'nii'`` is not available for ``'seyfert'`` and ``'liner'``. All the global masks are
            unique (a spaxel can only belong to one of them) with the exception of ``'agn'``, which
            intersects with ``'seyfert'`` and ``'liner'``. Additionally, if ``return_figure=True``,
            ``bpt_kewley06`` will return a tuple, the first elemnt of which is the dictionary of
            classification masks, and the second the matplotlib figure for the generated plot.

    Example:
        >>> maps_8485_1901 = Maps(plateifu='8485-1901')
        >>> bpt_masks = bpt_kewley06(maps_8485_1901)

        Gets the global mask for star forming spaxels

        >>> sf = bpt_masks['sf']['global']

        Gets the seyfert mask based only on the SII/Halpha vs OIII/Hbeta diagnostics

        >>> seyfert_sii = bpt_masks['seyfert']['sii']

    """

    if 'snr' in kwargs:
        warnings.warn(
            'snr is deprecated. Use snr_min instead. '
            'snr will be removed in a future version of marvin',
            MarvinDeprecationWarning)
        snr_min = kwargs.pop('snr')
    elif len(kwargs.keys()) > 0:
        raise MarvinError('unknown keyword {0}'.format(list(kwargs.keys())[0]))

    # Gets the necessary emission line maps
    oiii = get_masked(maps, 'oiii_5008', snr=get_snr(snr_min, 'oiii'))
    nii = get_masked(maps, 'nii_6585', snr=get_snr(snr_min, 'nii'))
    ha = get_masked(maps, 'ha_6564', snr=get_snr(snr_min, 'ha'))
    hb = get_masked(maps, 'hb_4862', snr=get_snr(snr_min, 'hb'))
    sii = get_masked(maps, 'sii_6718', snr=get_snr(snr_min, 'sii'))
    oi = get_masked(maps, 'oi_6302', snr=get_snr(snr_min, 'oi'))

    # Calculate masked logarithms
    log_oiii_hb = np.ma.log10(oiii / hb)
    log_nii_ha = np.ma.log10(nii / ha)
    log_sii_ha = np.ma.log10(sii / ha)
    log_oi_ha = np.ma.log10(oi / ha)

    # Calculates masks for each emission mechanism according to the paper boundaries.
    # The log_nii_ha < 0.05, log_sii_ha < 0.32, etc are necessary because the classification lines
    # diverge and we only want the region before the asymptota.
    sf_mask_nii = ((log_oiii_hb < kewley_sf_nii(log_nii_ha)) &
                   (log_nii_ha < 0.05)).filled(False)
    sf_mask_sii = ((log_oiii_hb < kewley_sf_sii(log_sii_ha)) &
                   (log_sii_ha < 0.32)).filled(False)
    sf_mask_oi = ((log_oiii_hb < kewley_sf_oi(log_oi_ha)) &
                  (log_oi_ha < -0.59)).filled(False)
    sf_mask = sf_mask_nii & sf_mask_sii & sf_mask_oi if use_oi else sf_mask_nii & sf_mask_sii

    comp_mask = ((log_oiii_hb > kewley_sf_nii(log_nii_ha)) & (log_nii_ha < 0.05)).filled(False) & \
                ((log_oiii_hb < kewley_comp_nii(log_nii_ha)) & (log_nii_ha < 0.465)).filled(False)
    comp_mask &= (sf_mask_sii & sf_mask_oi) if use_oi else sf_mask_sii

    agn_mask_nii = ((log_oiii_hb > kewley_comp_nii(log_nii_ha)) |
                    (log_nii_ha > 0.465)).filled(False)
    agn_mask_sii = ((log_oiii_hb > kewley_sf_sii(log_sii_ha)) |
                    (log_sii_ha > 0.32)).filled(False)
    agn_mask_oi = ((log_oiii_hb > kewley_sf_oi(log_oi_ha)) |
                   (log_oi_ha > -0.59)).filled(False)
    agn_mask = agn_mask_nii & agn_mask_sii & agn_mask_oi if use_oi else agn_mask_nii & agn_mask_sii

    seyfert_mask_sii = agn_mask & (kewley_agn_sii(log_sii_ha) <
                                   log_oiii_hb).filled(False)
    seyfert_mask_oi = agn_mask & (kewley_agn_oi(log_oi_ha) <
                                  log_oiii_hb).filled(False)
    seyfert_mask = seyfert_mask_sii & seyfert_mask_oi if use_oi else seyfert_mask_sii

    liner_mask_sii = agn_mask & (kewley_agn_sii(log_sii_ha) >
                                 log_oiii_hb).filled(False)
    liner_mask_oi = agn_mask & (kewley_agn_oi(log_oi_ha) >
                                log_oiii_hb).filled(False)
    liner_mask = liner_mask_sii & liner_mask_oi if use_oi else liner_mask_sii

    # The invalid mask is the combination of spaxels that are invalid in all of the emission maps
    invalid_mask_nii = ha.mask | oiii.mask | nii.mask | hb.mask
    invalid_mask_sii = ha.mask | oiii.mask | sii.mask | hb.mask
    invalid_mask_oi = ha.mask | oiii.mask | oi.mask | hb.mask

    invalid_mask = ha.mask | oiii.mask | nii.mask | hb.mask | sii.mask
    if use_oi:
        invalid_mask |= oi.mask

    # The ambiguous mask are spaxels that are not invalid but don't fall into any of the
    # emission mechanism classifications.
    ambiguous_mask = ~(sf_mask | comp_mask | seyfert_mask
                       | liner_mask) & ~invalid_mask

    sf_classification = {
        'global': sf_mask,
        'nii': sf_mask_nii,
        'sii': sf_mask_sii
    }

    comp_classification = {'global': comp_mask, 'nii': comp_mask}

    agn_classification = {
        'global': agn_mask,
        'nii': agn_mask_nii,
        'sii': agn_mask_sii
    }

    seyfert_classification = {'global': seyfert_mask, 'sii': seyfert_mask_sii}

    liner_classification = {'global': liner_mask, 'sii': liner_mask_sii}

    invalid_classification = {
        'global': invalid_mask,
        'nii': invalid_mask_nii,
        'sii': invalid_mask_sii
    }

    ambiguous_classification = {'global': ambiguous_mask}

    if use_oi:
        sf_classification['oi'] = sf_mask_oi
        agn_classification['oi'] = agn_mask_oi
        seyfert_classification['oi'] = seyfert_mask_oi
        liner_classification['oi'] = liner_mask_oi
        invalid_classification['oi'] = invalid_mask_oi

    bpt_return_classification = {
        'sf': sf_classification,
        'comp': comp_classification,
        'agn': agn_classification,
        'seyfert': seyfert_classification,
        'liner': liner_classification,
        'invalid': invalid_classification,
        'ambiguous': ambiguous_classification
    }

    if not return_figure:
        return bpt_return_classification

    # Does all the plotting
    with plt.style.context('seaborn-darkgrid'):
        fig, grid_bpt, gal_bpt = _get_kewley06_axes(use_oi=use_oi)

    sf_kwargs = {
        'marker': 's',
        's': 12,
        'color': 'c',
        'zorder': 50,
        'alpha': 0.7,
        'lw': 0.0
    }
    sf_handler = grid_bpt[0].scatter(log_nii_ha[sf_mask], log_oiii_hb[sf_mask],
                                     **sf_kwargs)
    grid_bpt[1].scatter(log_sii_ha[sf_mask], log_oiii_hb[sf_mask], **sf_kwargs)

    comp_kwargs = {
        'marker': 's',
        's': 12,
        'color': 'g',
        'zorder': 45,
        'alpha': 0.7,
        'lw': 0.0
    }
    comp_handler = grid_bpt[0].scatter(log_nii_ha[comp_mask],
                                       log_oiii_hb[comp_mask], **comp_kwargs)
    grid_bpt[1].scatter(log_sii_ha[comp_mask], log_oiii_hb[comp_mask],
                        **comp_kwargs)

    seyfert_kwargs = {
        'marker': 's',
        's': 12,
        'color': 'r',
        'zorder': 40,
        'alpha': 0.7,
        'lw': 0.0
    }
    seyfert_handler = grid_bpt[0].scatter(log_nii_ha[seyfert_mask],
                                          log_oiii_hb[seyfert_mask],
                                          **seyfert_kwargs)
    grid_bpt[1].scatter(log_sii_ha[seyfert_mask], log_oiii_hb[seyfert_mask],
                        **seyfert_kwargs)

    liner_kwargs = {
        'marker': 's',
        's': 12,
        'color': 'm',
        'zorder': 35,
        'alpha': 0.7,
        'lw': 0.0
    }
    liner_handler = grid_bpt[0].scatter(log_nii_ha[liner_mask],
                                        log_oiii_hb[liner_mask],
                                        **liner_kwargs)
    grid_bpt[1].scatter(log_sii_ha[liner_mask], log_oiii_hb[liner_mask],
                        **liner_kwargs)

    amb_kwargs = {
        'marker': 's',
        's': 12,
        'color': '0.6',
        'zorder': 30,
        'alpha': 0.7,
        'lw': 0.0
    }
    amb_handler = grid_bpt[0].scatter(log_nii_ha[ambiguous_mask],
                                      log_oiii_hb[ambiguous_mask],
                                      **amb_kwargs)
    grid_bpt[1].scatter(log_sii_ha[ambiguous_mask],
                        log_oiii_hb[ambiguous_mask], **amb_kwargs)

    if use_oi:
        grid_bpt[2].scatter(log_oi_ha[sf_mask], log_oiii_hb[sf_mask],
                            **sf_kwargs)
        grid_bpt[2].scatter(log_oi_ha[comp_mask], log_oiii_hb[comp_mask],
                            **comp_kwargs)
        grid_bpt[2].scatter(log_oi_ha[seyfert_mask], log_oiii_hb[seyfert_mask],
                            **seyfert_kwargs)
        grid_bpt[2].scatter(log_oi_ha[liner_mask], log_oiii_hb[liner_mask],
                            **liner_kwargs)
        grid_bpt[2].scatter(log_oi_ha[ambiguous_mask],
                            log_oiii_hb[ambiguous_mask], **amb_kwargs)

    # Creates the legend
    grid_bpt[0].legend([
        sf_handler, comp_handler, seyfert_handler, liner_handler, amb_handler
    ], ['Star-forming', 'Composite', 'Seyfert', 'LINER', 'Ambiguous'],
                       ncol=2,
                       loc='upper left',
                       frameon=True,
                       labelspacing=0.1,
                       columnspacing=0.1,
                       handletextpad=0.1,
                       fontsize=9)

    # Creates a RGB image of the galaxy, and sets the colours of the spaxels to match the
    # classification masks
    gal_rgb = np.zeros((ha.shape[0], ha.shape[1], 3), dtype=np.uint8)

    for ii in [1, 2]:  # Cyan
        gal_rgb[:, :, ii][sf_mask] = 255

    gal_rgb[:, :, 1][comp_mask] = 128  # Green

    gal_rgb[:, :, 0][seyfert_mask] = 255  # Red

    # Magenta
    gal_rgb[:, :, 0][liner_mask] = 255
    gal_rgb[:, :, 2][liner_mask] = 255

    for ii in [0, 1, 2]:
        gal_rgb[:, :, ii][invalid_mask] = 255  # White
        gal_rgb[:, :, ii][ambiguous_mask] = 169  # Grey

    # Shows the image.
    gal_bpt.imshow(gal_rgb,
                   origin='lower',
                   aspect='auto',
                   interpolation='nearest')

    gal_bpt.set_xlim(0, ha.shape[1] - 1)
    gal_bpt.set_ylim(0, ha.shape[0] - 1)
    gal_bpt.set_xlabel('x [spaxels]')
    gal_bpt.set_ylabel('y [spaxels]')

    return (bpt_return_classification, fig)
Example #24
0
def _get_model_cube(name, use_file=False, release=None, **kwargs):
    """Retrieves a Marvin ModelCube object."""

    model_cube = None
    results = {}

    drpver, dapver = config.lookUpVersions(release)

    # parse name into either mangaid or plateifu
    try:
        idtype = parseIdentifier(name)
    except Exception as err:
        results['error'] = 'Failed to parse input name {0}: {1}'.format(name, str(err))
        return model_cube, results

    filename = None
    plateifu = None
    mangaid = None

    bintype = kwargs.pop('bintype')
    template = kwargs.pop('template')

    try:
        if use_file:

            if idtype == 'mangaid':
                plate, ifu = mangaid2plateifu(name, drpver=drpver)
            elif idtype == 'plateifu':
                plate, ifu = name.split('-')

            if Path is not None:

                daptype = '{0}-{1}'.format(bintype, template)

                filename = Path().full('mangadap5', ifu=ifu,
                                       drpver=drpver,
                                       dapver=dapver,
                                       plate=plate, mode='LOGCUBE',
                                       daptype=daptype)
                assert os.path.exists(filename), 'file not found.'
            else:
                raise MarvinError('cannot create path for MaNGA cube.')

        else:

            if idtype == 'plateifu':
                plateifu = name
            elif idtype == 'mangaid':
                mangaid = name
            else:
                raise MarvinError('invalid plateifu or mangaid: {0}'.format(idtype))

        model_cube = ModelCube(filename=filename, mangaid=mangaid, plateifu=plateifu,
                               release=release, template=template, bintype=bintype, **kwargs)

        results['status'] = 1

    except Exception as err:

        results['error'] = 'Failed to retrieve ModelCube {0}: {1}'.format(name, str(err))

    return model_cube, results
Example #25
0
File: cube.py Project: rgcl/marvin
    def _get_spaxel_quantities(self, x, y):
        """Returns a dictionary of spaxel quantities."""

        cube_quantities = FuzzyDict({})

        if self.data_origin == 'db':

            session = marvin.marvindb.session
            datadb = marvin.marvindb.datadb

        if self.data_origin == 'file' or self.data_origin == 'db':

            # Stores a dictionary of (table, row)
            _db_rows = {}

            for dm in self.datamodel.datacubes + self.datamodel.spectra:

                data = {'value': None, 'ivar': None, 'mask': None, 'std': None}

                for key in data:

                    if key == 'ivar':
                        if dm in self.datamodel.spectra or not dm.has_ivar():
                            continue
                    if key == 'mask':
                        if dm in self.datamodel.spectra or not dm.has_mask():
                            continue
                    if key == 'std':
                        if dm in self.datamodel.datacubes or not dm.has_std():
                            continue

                    if self.data_origin == 'file':

                        extname = dm.fits_extension(None if key == 'value' else key)

                        if dm in self.datamodel.datacubes:
                            data[key] = self.data[extname].data[:, y, x]
                        else:
                            data[key] = self.data[extname].data

                    elif self.data_origin == 'db':

                        colname = dm.db_column(None if key == 'value' else key)

                        if dm in self.datamodel.datacubes:

                            if 'datacubes' not in _db_rows:
                                _db_rows['datacubes'] = session.query(datadb.Spaxel).filter(
                                    datadb.Spaxel.cube == self.data,
                                    datadb.Spaxel.x == x, datadb.Spaxel.y == y).one()

                            spaxel_data = getattr(_db_rows['datacubes'], colname)

                        else:

                            if 'spectra' not in _db_rows:
                                _db_rows['spectra'] = session.query(datadb.Cube).filter(
                                    datadb.Cube.pk == self.data.pk).one()

                            spaxel_data = getattr(_db_rows['spectra'], colname, None)

                        # In case the column was empty in the DB. At some point
                        # this can be removed.
                        if spaxel_data is None:
                            warnings.warn('cannot find {!r} data for {!r}. '
                                          'Maybe the data is not in the DB.'.format(
                                              colname, self.plateifu), MarvinUserWarning)
                            cube_quantities[dm.name] = None
                            continue

                        data[key] = np.array(spaxel_data)

                cube_quantities[dm.name] = Spectrum(data['value'],
                                                    ivar=data['ivar'],
                                                    mask=data['mask'],
                                                    std=data['std'],
                                                    wavelength=self._wavelength,
                                                    unit=dm.unit,
                                                    pixmask_flag=dm.pixmask_flag)

        if self.data_origin == 'api':

            params = {'release': self._release}
            url = marvin.config.urlmap['api']['getCubeQuantitiesSpaxel']['url']

            try:
                response = self._toolInteraction(url.format(name=self.plateifu,
                                                            x=x, y=y, params=params))
            except Exception as ee:
                raise MarvinError('found a problem when checking if remote cube '
                                  'exists: {0}'.format(str(ee)))

            data = response.getData()

            for dm in self.datamodel.datacubes + self.datamodel.spectra:

                if data[dm.name]['value'] is None:
                    warnings.warn('cannot find {!r} data for {!r}. '
                                  'Maybe the data is not in the DB.'.format(
                                      dm.name, self.plateifu), MarvinUserWarning)
                    cube_quantities[dm.name] = None
                    continue

                cube_quantities[dm.name] = Spectrum(data[dm.name]['value'],
                                                    ivar=data[dm.name]['ivar'],
                                                    mask=data[dm.name]['mask'],
                                                    wavelength=data['wavelength'],
                                                    unit=dm.unit,
                                                    pixmask_flag=dm.pixmask_flag)

        return cube_quantities
Example #26
0
File: base.py Project: zpace/marvin
    def parameters(self, value):
        """Raises an error if trying to set groups directly."""

        raise MarvinError('cannot set groups directly. Use add_parameters() instead.')
Example #27
0
def save(obj, path=None, overwrite=False):
    """Pickles the object.

    If ``path=None``, uses the default location of the file in the tree
    but changes the extension of the file to ``.mpf`` (MaNGA Pickle File).
    Returns the path of the saved pickle file.

    Parameters:
        obj:
            Marvin object to pickle.
        path (str):
            Path of saved file. Default is ``None``.
        overwrite (bool):
            If ``True``, overwrite existing file. Default is ``False``.

    Returns:
        str:
            Path of saved file.
    """

    from ..tools.core import MarvinToolsClass

    if path is None:
        assert isinstance(
            obj,
            MarvinToolsClass), 'path=None is only allowed for core objects.'
        path = obj._getFullPath()
        assert isinstance(path, string_types), 'path must be a string.'
        if path is None:
            raise MarvinError('cannot determine the default path in the '
                              'tree for this file. You can overcome this '
                              'by calling save with a path keyword with '
                              'the path to which the file should be saved.')

        # Replaces the extension (normally fits) with mpf
        if '.fits.gz' in path:
            path = path.strip('.fits.gz')
        else:
            path = os.path.splitext(path)[0]

        path += '.mpf'

    path = os.path.realpath(os.path.expanduser(path))

    if os.path.isdir(path):
        raise MarvinError('path must be a full route, including the filename.')

    if os.path.exists(path) and not overwrite:
        warnings.warn('file already exists. Not overwriting.',
                      MarvinUserWarning)
        return

    dirname = os.path.dirname(path)
    if not os.path.exists(dirname):
        os.makedirs(dirname)

    try:
        with open(path, 'wb') as fout:
            pickle.dump(obj, fout, protocol=-1)
    except Exception as ee:
        if os.path.exists(path):
            os.remove(path)
        raise MarvinError('error found while pickling: {0}'.format(str(ee)))

    return path