.. _marvin-getting_started: Getting Started =============== Here is a guide to how Marvin works and the basics of getting set up to use Marvin for accessing MaNGA data. If you are unfamiliar with MaNGA data, see the `MaNGA Survey `_ for an introduction into the survey. See `MaNGA in DR17 `_ for details on the latest MaNGA data release. .. _getstart-tools: .. py:currentmodule:: marvin.tools .. note:: In all of the code blocks on this page, and throughout the rest of the documentation, a `>>>` may appear in the top right corner. Clicking this icon will allow you to copy the displayed code to paste it in your local session. With Tools ---------- Marvin Galaxy Tools are the four main classes (`~cube.Cube`, `~rss.RSS`, `~maps.Maps`, and `~modelcube.ModelCube`) associated with the analogous DRP and DAP data products, the `Astropy quantities ` representing multidimensional data, and a variety of utilities and mixins that provide additional functionality. Sub-region galaxy tools (`~spaxel.Spaxel` and binning information) are explained :ref:`in their own section `. The four main Tools classes inherit from `~tools.core.MarvinToolsClass` and thus much of their functionality and logic is shared. In this section we will prominently use the `~cube.Cube` but most of what we explain here also applies to the remaining Tools. All the Tools classes can be accessed from the :ref:`marvin.tools ` module. Let's load a DRP Cube :: >>> import marvin >>> my_cube = marvin.tools.Cube('8485-1901') >>> my_cube Depending on whether you have the file on disk or not, the access mode will be ``'local'`` or ``'remote'``. Regardless of that, the way we interact with the object will be the same. All tools provide quick access to some basic metadata :: >>> print(my_cube.filename, my_cube.plateifu, my_cube.mangaid, my_cube.release) /Users/albireo/Documents/MaNGA/mangawork/manga/spectro/redux/v3_1_1/8485/stack/manga-8485-1901-LOGCUBE.fits.gz 8485-1901 1-209232 DR17 >>> print(my_cube.ra, my_cube.dec) 232.544703894 48.6902009334 .. note:: When accessing remote data in Marvin, i.e. when the mode is "remote", the ``filename`` attribute on the Marvin Tool instance will be None. Similarly we can access the `FITS header ` of the file and the `Astropy WCS ` object :: >>> my_cube.header XTENSION= 'IMAGE ' / IMAGE extension BITPIX = -32 / Number of bits per data pixel NAXIS = 3 / Number of data axes NAXIS1 = 74 / NAXIS2 = 74 / >>> my_cube.wcs WCS Keywords Number of WCS axes: 3 CTYPE : 'RA---TAN' 'DEC--TAN' 'WAVE-LOG' CRVAL : 232.5447 48.690201 3.62159598486e-07 CRPIX : 18.0 18.0 1.0 CD1_1 CD1_2 CD1_3 : -0.000138889 0.0 0.0 CD2_1 CD2_2 CD2_3 : 0.0 0.000138889 0.0 CD3_1 CD3_2 CD3_3 : 0.0 0.0 8.33903304339e-11 NAXIS : 34 34 4563 Furthermore, we can access the :ref:`datamodel ` of the cube file, which show us what extensions are available, how they are named in Marvin, and what they contain :: >>> datamodel = my_cube.datamodel >>> datamodel >>> datamodel.datacubes [, , ] >>> datamodel.spectra [, ] This tells us that this cube has two associated 3D datacubes, ``'flux'``, ``'dispersion'``, and ``'dispersion_prepixel'``, and two associated spectra, ``'spectral_resolution'`` and ``'spectral_resolution_prepixel'``, as well as their associated units. We can get a desciption of what each of them :: >>> datamodel.datacubes.flux.name 'flux' >>> datamodel.datacubes.flux.description '3D rectified cube' In ``my_cube``, we can use the name of each of these datacubes and spectra to access the associated data quantity. Let's get the cube flux :: >>> # access the full 3d cube >>> flux = my_cube.flux >>> flux The flux is represented as a 3D array with units. We can also access the inverse variance and the mask using ``flux.ivar`` and ``flux.mask``, respectively. We can slice this datacube to get another datacube :: >>> # use array indexing to slice a cube >>> flux[:, 10:20, 10:20] Or get a single spectrum and plot it:: >>> # get a single spaxel from the cube >>> spectrum = flux[:, 20, 25] >>> spectrum >>> spectrum.plot(show_std=True) .. plot:: :align: center import marvin my_cube = marvin.tools.Cube('8485-1901') spectrum = my_cube[15, 15].flux ax = spectrum.plot(show_std=True) ax.set_xlim(6000, 8000) We will talk more about quantities in the :ref:`marvin-quantities` section, and about more advance plotting in :ref:`marvin-plotting`. From a DRP cube we can get the associated DAP `~marvin.tools.maps.Maps` object for a certain bintype :: >>> hyb_maps = my_cube.getMaps(bintype='HYB10') A `~marvin.tools.maps.Maps` behaves very similarly to a `~marvin.tools.cube.Cube` and everything we have discussed above will still work. Instead of datacubes and spectra, a Maps object contains a set of 2D quantities called `~marvin.tools.quantities.map.Map`, each one of them representing a different ``property`` measured by the DAP. One can get a full list of all the properties available using the :ref:`datamodel ` :: >>> hyb_maps.datamodel [, , , , , , , , ... ] Note that some properties such as ``'spx_skycoo'`` have multiple channels (in this case the on-sky x and y coordinates). We can get more information about a property :: >>> hyb_maps.datamodel.spx_skycoo_on_sky_x.description 'Offsets of each spaxel from the galaxy center. Channel = On-sky X.' See the :ref:`datamodel ` section for more information on how to use this feature. We can retrieve the map associated to a specific property directly from the `~marvin.tools.maps.Maps` instance. For example, let's get the H :math:`\alpha` emission line flux (fitted by a Gaussian) from a different Maps file :: >>> my_cube = marvin.tools.Maps('7443-12703') >>> ha = my_cube.emline_gflux_ha_6564 >>> ha [[0. 0. 0. ... 0. 0. 0.] [0. 0. 0. ... 0. 0. 0.] [0. 0. 0. ... 0. 0. 0.] ... [0. 0. 0. ... 0. 0. 0.] [0. 0. 0. ... 0. 0. 0.] [0. 0. 0. ... 0. 0. 0.]] 1e-17 erg / (cm2 s spaxel) .. hint:: In IPython, you can use tab-completion to autocomplete the name of the property. If you press tab after writing ``hyb_maps.emline_`` you will get a list of all the emission line properties available. `~marvin.tools.quantities.map.Map` quantities are similar to `~marvin.tools.quantities.datacube.DataCube` but wrap a 2D array. We can plot the Map as :: >>> fig, ax = ha.plot() .. plot:: :align: center import marvin my_maps = marvin.tools.Maps('7443-12703', bintype='HYB10') my_maps.emline_gflux_ha_6564.plot() Note that the `~marvin.tools.quantities.map.Map.plot` method returns the matplotlib `~matplotlib.figure.Figure` and `~matplotlib.axes.Axes` for the plot. We can use those to modify or save the plot. :ref:`Marvin plotting routines ` try to select the best parameters, colour maps, and dynamic ranges. You can modify those by passing extra arguments to `~marvin.tools.quantities.map.Map.plot`. You can learn more in the :ref:`Map plotting ` section. We will talk about the `~marvin.tools.quantities.map.Map` class in detail in :ref:`marvin-quantities` and in :ref:`marvin-map`. Let's take a step back and go back to ``hyb_maps``, our `~marvin.tools.maps.Maps` instance. We can access the `targeting bits <~marvin.tools.core.MarvinToolsClass.target_flags>` for that galaxy (for an introduction to maskbits check `this page `_) :: >>> hyb_maps.target_flags [, , ] Note that in this case the galaxy belongs to the primary sample from the final target selection (``PRIMARY_v1_2_0``) as well as to the primary and colour enhanced samples from several commissioning target selections. The galaxy does not have any ancillary bit (``manga_target3``). Similarly, we can access quality flags, which indicate us if there is something we need to know about the data :: >>> hyb_maps.quality_flag In this case the ``MANGA_DAPQUAL`` maskbit does not have any bit activated, which means the data is safe to use. See the :ref:`Maskbits ` section for more information. For each target we can also access additional catalogue data: the associated parameters from the `NASA Sloan Atlas `_, and the `DAPall `_ file :: >>> my_cube.nsa {'iauname': 'J151806.10+424438.0', 'field': 213, 'run': 3918, 'camcol': 3, 'version': 'v1_0_1', 'nsaid': 684509, 'nsaid_v1b': 230855, 'z': 0.0402719, 'zdist': 0.0406307, ... } >>> my_maps.dapall {'plate': 7443, 'ifudesign': 12703, 'plateifu': '7443-12703', 'mangaid': '12-193481', 'drpallindx': 4319, 'mode': 'CUBE', 'daptype': 'HYB10-MILESHC-MASTARSSP', ... } The NSA and DAPall catalogues are implemented as mixins via `~marvin.tools.mixins.nsa.NSAMixIn` and `~marvin.tools.mixins.dapall.DAPAllMixIn`, respectively. While Marvin allows you to access data remotely, frequently you will find that you want to download the file associated to an object so that you can access it more quickly in the future. We can do that using the `MarvinToolsClass.download ` method. Let's try to load a cube that we know we do not have in out hard drive :: >>> remote_cube = marvin.tools.Cube('8485-1902') >>> remote_cube >>> remote_cube.download() SDSS_ACCESS> syncing... please wait SDSS_ACCESS> Done! Now we can try loading it again :: >>> new_cube = marvin.tools.Cube('8485-1902') >>> new_cube >>> new_cube.filename '/Users/albireo/Documents/MaNGA/mangawork/manga/spectro/redux/v2_3_1/8485/stack/manga-8485-1902-LOGCUBE.fits.gz' The cube has now been loaded from the file we just downloaded! You can find the file in its corresponding location in your local SAS. Finally, we can extract one or more `~marvin.tools.spaxel.Spaxel` object from a Galaxy Tool. We can either use the standard array slicing notation (0-indexed, origin of coordinates in the lower left corner of the array) :: >>> spaxel = new_cube[15, 10] >>> spaxel or we can use `~cube.Cube.getSpaxel`, which accepts multiple arguments (refer to the method's documentation). Note that by default, ``(x, y)`` coordinates passed to `~cube.Cube.getSpaxel` are measured from the centre of the array :: >>> central_spaxel = new_cube.getSpaxel(x=0, y=0) >>> central_spaxel `~marvin.tools.spaxel.Spaxel` and `~marvin.tools.spaxel.Bin` will be treated in detail in the :ref:`marvin--subregion-tools` section. .. _marvin-quantities: Working with Astropy Quantities ------------------------------- Marvin presents scientific data in the form of `Astropy Quantities `__. A Quantity is essentially a number with an associated physical unit. In Marvin we expand on that concept and extend the Quantities with a mask, an inverse variance (`why do we use ivar in MaNGA? `__) and, when relevant, the associated wavelength. Marvin Quantities also provide useful methods to, for instance, calculate the SNR or plot the value. Marvin provides Quantities for 1D (`~marvin.tools.quantities.spectrum.Spectrum`, `~marvin.tools.quantities.analysis_props.AnalysisProperty`), 2D (`~marvin.tools.quantities.map.Map`), and 3D data (`~marvin.tools.quantities.datacube.DataCube`). All Quantities behave similarly. Let's start by getting a datacube (3D Quantity) from a `~marvin.tools.cube.Cube` object :: >>> my_cube = marvin.tools.Cube('7443-12701') >>> flux = my_cube.flux >>> flux >>> flux.wavelength A slice of a `~marvin.tools.quantities.datacube.DataCube` is another datacube :: >>> flux_section = flux[1000:2000, 15:20, 15:20] >>> flux_section Note that in addition to the array the `~marvin.tools.quantities.datacube.DataCube` has associated units (:math:`{\rm 10^{-17}\,erg\,cm^{-2}\,s^{-1}\,spaxel}`). We can get the value, unit, and the scale as :: >>> flux_section.value array([[[0.0484641 , 0.0455479 , 0.0421016 , 0.0391036 , 0.0412236 ], [0.048177 , 0.0437978 , 0.0384898 , 0.0335415 , 0.0345823 ], [0.0358995 , 0.0385949 , 0.0338827 , 0.0293836 , 0.0337355 ], [0.0177076 , 0.024134 , 0.0270703 , 0.0271202 , 0.0312836 ], [0.0052256 , 0.0119592 , 0.0181215 , 0.0243616 , 0.0311569 ]], ..., [[0.0448547 , 0.0435139 , 0.041652 , 0.0415161 , 0.0468557 ], [0.0408965 , 0.0431359 , 0.0441348 , 0.0448875 , 0.0507026 ], [0.0375406 , 0.0409193 , 0.0423735 , 0.0434993 , 0.0484709 ], [0.0306319 , 0.0335499 , 0.0357318 , 0.0381165 , 0.0422256 ], [0.0261617 , 0.0271262 , 0.0294177 , 0.033631 , 0.039794 ]]]) >>> flux_section.unit Unit("1e-17 erg / (Angstrom cm2 s spaxel)") >>> flux_section.unit.scale 1e-17 It's important to pay attention to the scale to convert to physical units. If you prefer to have the scale included in the value you can use the `~marvin.tools.quantities.base_quantity.QuantityMixIn.descale` method :: >>> flux.value[1000, 15, 15] 0.0484641 >>> descaled = flux.descale() >>> descaled.value[1000, 15, 15] 4.84641e-19 We can also access the associated inverse variance or convert it to error, as well as compute the signal-to-noise ratio :: >>> flux.ivar[1000, 15, 15] 3654.32 >>> flux.error[1000, 15, 15] >>> flux.snr[1000, 15, 15] 2.9297019457938314 The mask associated with the values is easily accessible via the ``mask`` attribute. We can also use the `~marvin.tools.quantities.base_quantity.QuantityMixIn.masked` method to return a Numpy `masked array `__ in which the values that should not be used have been masked away :: >>> flux_section.masked masked_array( data=[[[--, 0.0455479, 0.0421016, 0.0391036, 0.0412236], [0.048177, 0.0437978, 0.0384898, 0.0335415, 0.0345823], [0.0358995, 0.0385949, 0.0338827, 0.0293836, 0.0337355], [0.0177076, 0.024134, 0.0270703, 0.0271202, 0.0312836], [0.0052256, 0.0119592, 0.0181215, 0.0243616, 0.0311569]], ..., [[--, 0.0435139, 0.041652, 0.0415161, 0.0468557], [0.0408965, 0.0431359, 0.0441348, 0.0448875, 0.0507026], [0.0375406, 0.0409193, 0.0423735, 0.0434993, 0.0484709], [0.0306319, 0.0335499, 0.0357318, 0.0381165, 0.0422256], [0.0261617, 0.0271262, 0.0294177, 0.033631, 0.039794]]], mask=[[[ True, False, False, False, False], [False, False, False, False, False], [False, False, False, False, False], [False, False, False, False, False], [False, False, False, False, False]], ..., [[ True, False, False, False, False], [False, False, False, False, False], [False, False, False, False, False], [False, False, False, False, False], [False, False, False, False, False]]], fill_value=1e+20) Quantities have an associated `~marvin.tools.quantities.base_quantity.QuantityMixIn.pixmask`, which provides a simple way to interact with the mask bits (for more information, go to the :ref:`marvin-maskbit` section) :: >>> flux.pixmask >>> flux.pixmask.get_mask('NOCOV') # Returns a mask of values with the NOCOV maskbit. array([[[1, 1, 1, ..., 1, 1, 1], [1, 1, 1, ..., 1, 1, 1], [1, 1, 1, ..., 1, 1, 1], ..., [1, 1, 1, ..., 1, 1, 1], [1, 1, 1, ..., 1, 1, 1], [1, 1, 1, ..., 1, 1, 1]], ..., [[1, 1, 1, ..., 1, 1, 1], [1, 1, 1, ..., 1, 1, 1], [1, 1, 1, ..., 1, 1, 1], ..., [1, 1, 1, ..., 1, 1, 1], [1, 1, 1, ..., 1, 1, 1], [1, 1, 1, ..., 1, 1, 1]]]) We can also slice a datacube to get a single spectrum :: >>> spectrum_20_20 = flux[:, 20, 20] >>> spectrum_20_20 In this case the returned Quantity is a 1D `~marvin.tools.quantities.spectrum.Spectrum`. This new Quantity behaves exactly as the `~marvin.tools.quantities.datacube.DataCube` but in this case we can also `~marvin.tools.quantities.spectrum.Spectrum.plot` the spectrum :: >>> spectrum_20_20.plot() .. plot:: :align: center :include-source: False import marvin my_cube = marvin.tools.Cube('8485-1901') flux = my_cube[20, 20].flux flux.plot() Let's now have a look at the Marvin 2D Quantity: the `~marvin.tools.quantities.map.Map`. :: >>> maps_obj = Maps('7443-12703') >>> ha = maps_obj.emline_gflux_ha_6564 [[0. 0. 0. ... 0. 0. 0.] [0. 0. 0. ... 0. 0. 0.] [0. 0. 0. ... 0. 0. 0.] ... [0. 0. 0. ... 0. 0. 0.] [0. 0. 0. ... 0. 0. 0.] [0. 0. 0. ... 0. 0. 0.]] 1e-17 erg / (cm2 s spaxel) We can still use all the tools we discussed above. For example, let's plot the signal-to-noise ratio :: >>> snr = ha.snr >>> plt.imshow(snr, origin='lower') .. plot:: :align: center :include-source: False import marvin import matplotlib.pyplot as plt maps_obj = marvin.tools.Maps('7443-12703') ha_snr = maps_obj.emline_gflux_ha_6564.snr plt.imshow(ha_snr, origin='lower') `Map` objects are a bit special, though, and we will discuss them in detail in :ref:`their own section `. Here, let's see how we can do "Map arithmetic" by calculating the :math:`{\rm H\alpha/H\beta}` ratio :: >>> hb = maps_obj.emline_gew_hb_4862 >>> ha_hb = ha / hb >>> ha_hb array([[nan, nan, nan, ..., nan, nan, nan], [nan, nan, nan, ..., nan, nan, nan], [nan, nan, nan, ..., nan, nan, nan], ..., [nan, nan, nan, ..., nan, nan, nan], [nan, nan, nan, ..., nan, nan, nan], [nan, nan, nan, ..., nan, nan, nan]], dtype=float32) '1e-17 erg / (Angstrom cm2 s spaxel)' >>> ha_hb.plot() .. plot:: :align: center :include-source: False import marvin maps_obj = marvin.tools.Maps('7443-12703') ha = maps_obj.emline_gflux_ha_6564 hb = maps_obj.emline_gew_hb_4862 ha_hb = ha / hb ha_hb.plot() `~marvin.tools.quantities.map.EnhancedMap` result from the arithmetic combination of two maps and take care of all the gritty details: error propagation, division by zero, maskbit propagation, etc. Finally, `~marvin.tools.quantities.analysis_props.AnalysisProperty` are 1D quantities associated with a value for a single spaxel on a `~marvin.tools.quantities.map.Map`. We will discuss them in depth when we talk about :ref:`marvin-subregion-tools`.