Exemple #1
0
def get_gaiadr3_dist(gaiadr2, gaia3):
    #vqdr3 = Vizier(columns=['Source','Plx','e_Plx', 'FG','e_FG','Gmag','e_Gmag', 'BPmag','e_BPmag', 'RPmag','e_RPmag', 'o_Gmag'], row_limit=5000)
    vqdr3_dist = Vizier(row_limit=5000)
    radius_search = 10.0 * u.arcsec
    try:
        #result_gaia_vizier_dr3=vqdr3.query_object("Gaia DR2 "+str(gaiadr2), catalog=["I/350/gaiaedr3"], radius=radius_search)
        result_dist = vqdr3_dist.query_object("Gaia DR2 " + str(gaiadr2),
                                              catalog=["I/352/gedr3dis"],
                                              radius=radius_search)
        #iline = np.where(result_gaia_vizier_dr3[0]['Source'] == int(gaia3))[0][0]
        #print(result_gaia_vizier_dr3[0]['Source','Plx','e_Plx', 'Gmag', 'e_Gmag', 'FG', 'e_FG', 'RPmag', 'e_RPmag', 'BPmag', 'e_BPmag'][iline])
        #    std_g_flux_norm1_v = result_gaia_vizier_dr3[0]['e_FG'][iline]*np.sqrt(result_gaia_vizier_dr3[0]['o_Gmag'][iline])/result_gaia_vizier_dr3[0]['FG'][iline]
        iline_d = np.where(result_dist[0]['Source'] == int(gaia3))[0][0]
        #print(result_dist[0]['Source','rgeo', 'rpgeo'][iline_d])
    except:
        try:
            #result_gaia_vizier_dr3=vqdr3.query_object("Gaia DR2 "+str(gaiadr2), catalog=["I/350/gaiaedr3"], radius=100*radius_search)
            result_dist = vqdr3_dist.query_object("Gaia DR2 " + str(gaiadr2),
                                                  catalog=["I/352/gedr3dis"],
                                                  radius=20 * radius_search)
            #iline = np.where(result_gaia_vizier_dr3[0]['Source'] == int(gaia3))[0][0]
            iline_d = np.where(result_dist[0]['Source'] == int(gaia3))[0][0]
        except:
            print("problem")
            return -1, -1, -1, -1
    #print("Ilines:", iline, iline_d)
    rgeo = result_dist[0][iline_d]["rgeo"]
    b_rgeo = result_dist[0][iline_d]["b_rgeo"]
    B_rgeo = result_dist[0][iline_d]["B_rgeo"]
    ergeo = np.max([rgeo - b_rgeo, B_rgeo - rgeo])
    rpgeo = result_dist[0][iline_d]["rgeo"]
    b_rpgeo = result_dist[0][iline_d]["b_rpgeo"]
    B_rpgeo = result_dist[0][iline_d]["B_rpgeo"]
    erpgeo = np.max([rpgeo - b_rpgeo, B_rpgeo - rpgeo])
    return rgeo, ergeo, rpgeo, erpgeo
def get_gaia_dr2_paralax(filename = 'gaiadr2/gaiaid_sc.rdb', fileout= 'gaiadr2/tmp.rdb', append= True):
  if os.path.isfile(fileout):
    filetmp = open(fileout,"r")
    strlines = filetmp.readlines()
    filetmp.close()
    name_tmp, gaiaid_tmp = np.loadtxt(fileout, unpack=True,usecols=(0,1), skiprows=2, delimiter="\t", dtype=str)
  else:
    print("Fresh start")
    gaiaid_tmp = []
    strlines = ['name\tgaia_id\tPlx\te_Plx\tGmag\te_Gmag\tRPmag\te_RPmag\tBPmag\te_BPmag\tFG\te_FG\tG_flux_std_n\n']
    strlines.append('----\t-------\t---\t-----\t----\t------\t-----\t-------\t-----\t-------\t--\t----\t------------\n')

  print("Result GAIA vizier")
  vq2 = Vizier(columns=['Source','Plx','e_Plx', 'FG','e_FG','Gmag','e_Gmag', 'BPmag','e_BPmag', 'RPmag','e_RPmag', 'o_Gmag'], row_limit=5000) 

  name, gaia_id = np.loadtxt(filename, unpack=True, usecols=(0,1), skiprows=2, delimiter="\t", dtype=str)
  radius_search = 10.0*u.arcsec

  #ist = 3127
#  for i,gaiadr2 in enumerate(gaia_id[ist:]):
    #i += ist
  for i,gaiadr2 in enumerate(gaia_id):
    print(i, len(gaia_id), name[i], gaiadr2)
    if gaiadr2 in gaiaid_tmp and name[i] in name_tmp:
      print("This one already in place")
      continue
    if gaiadr2 == "-1":
      print(name[i], "No gaia id dr2")
      strlines.append('%s\t%s\t-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\n' % (name[i], gaiadr2))
    else:
      result_gaia_vizier=vq2.query_object("Gaia DR2 "+str(gaiadr2), catalog=["I/345/gaia2"], radius=radius_search*15.)
      try:
        iline = np.where(result_gaia_vizier[0]['Source'] == int(gaiadr2))[0][0]
        print(result_gaia_vizier[0]['Source','Plx','e_Plx', 'Gmag', 'e_Gmag', 'FG', 'e_FG', 'RPmag', 'e_RPmag', 'BPmag', 'e_BPmag'][iline])
        std_g_flux_norm1_v = result_gaia_vizier[0]['e_FG'][iline]*np.sqrt(result_gaia_vizier[0]['o_Gmag'][iline])/result_gaia_vizier[0]['FG'][iline]
      except IndexError:
        result_gaia_vizier=vq2.query_object("Gaia DR2 "+str(gaiadr2), catalog=["I/345/gaia2"], radius=radius_search*25.)
        try:
          iline = np.where(result_gaia_vizier[0]['Source'] == int(gaiadr2))[0][0]
          print(result_gaia_vizier[0]['Source','Plx','e_Plx', 'Gmag', 'e_Gmag', 'FG', 'e_FG', 'RPmag', 'e_RPmag', 'BPmag', 'e_BPmag'][iline])
          std_g_flux_norm1_v = result_gaia_vizier[0]['e_FG'][iline]*np.sqrt(result_gaia_vizier[0]['o_Gmag'][iline])/result_gaia_vizier[0]['FG'][iline]
        except IndexError:
          result_gaia_vizier =  [Table( [[-1], [-1],[-1], [-1] ,[-1], [-1] ,[-1] ,[-1]   ,[-1] , [-1]    ] ,
                                   names=('Plx','e_Plx','Gmag','e_Gmag','FG','e_FG','RPmag','e_RPmag','BPmag','e_BPmag')) ]
          std_g_flux_norm1_v = -1
          iline=0
      print("Iline2:", iline)
      strlines.append('%s\t%s\t%7.4f\t%7.4f\t%7.4f\t%7.4f\t%7.4f\t%7.4f\t%7.4f\t%7.4f\t%.3e\t%.3e\t%10.7f\n' % (name[i], gaiadr2, 
        result_gaia_vizier[0]['Plx'][iline], result_gaia_vizier[0]['e_Plx'][iline],
        result_gaia_vizier[0]['Gmag'][iline], result_gaia_vizier[0]['e_Gmag'][iline],
        result_gaia_vizier[0]['RPmag'][iline], result_gaia_vizier[0]['e_RPmag'][iline],
        result_gaia_vizier[0]['BPmag'][iline], result_gaia_vizier[0]['e_BPmag'][iline],
        result_gaia_vizier[0]['FG'][iline], result_gaia_vizier[0]['e_FG'][iline],
        std_g_flux_norm1_v))
      print(strlines[-1])
  fileo = open('gaiadr2/tmp2.rdb', "w")
  for line in strlines:
    fileo.write(line)
  fileo.close()
Exemple #3
0
def get_gaiadr3_data(gaiadr2, gaia3, name_search=None):
    vq2 = Vizier(columns=[
        'Source', 'Plx', 'e_Plx', 'FG', 'e_FG', 'Gmag', 'e_Gmag', 'BPmag',
        'e_BPmag', 'RPmag', 'e_RPmag', 'o_Gmag'
    ],
                 row_limit=5000)
    radius_search = 10.0 * u.arcsec
    if name_search is None:
        result_gaia_vizier_dr3 = vq2.query_object("Gaia DR2 " + str(gaiadr2),
                                                  catalog=["I/350/gaiaedr3"],
                                                  radius=radius_search * 15.)
    else:
        result_gaia_vizier_dr3 = vq2.query_object(name_search,
                                                  catalog=["I/350/gaiaedr3"],
                                                  radius=radius_search * 15.)
    try:
        iline3 = np.where(
            result_gaia_vizier_dr3[0]['Source'] == int(gaia3))[0][0]
        print(result_gaia_vizier_dr3[0]['Source', 'Plx', 'e_Plx', 'Gmag',
                                        'e_Gmag', 'FG', 'e_FG', 'RPmag',
                                        'e_RPmag', 'BPmag', 'e_BPmag'][iline3])
        std_g_flux_norm1_v = result_gaia_vizier_dr3[0]['e_FG'][
            iline3] * np.sqrt(result_gaia_vizier_dr3[0]['o_Gmag'][iline3]
                              ) / result_gaia_vizier_dr3[0]['FG'][iline3]
    except IndexError:
        result_gaia_vizier_dr3 = vq2.query_object("Gaia DR2 " + str(gaiadr2),
                                                  catalog=["I/350/gaiaedr3"],
                                                  radius=radius_search * 25.)
        try:
            iline3 = np.where(
                result_gaia_vizier_dr3[0]['Source'] == int(gaia3))[0][0]
            print(result_gaia_vizier_dr3[0]['Source', 'Plx', 'e_Plx', 'Gmag',
                                            'e_Gmag', 'FG', 'e_FG', 'RPmag',
                                            'e_RPmag', 'BPmag',
                                            'e_BPmag'][iline3])
            std_g_flux_norm1_v = result_gaia_vizier_dr3[0]['e_FG'][
                iline3] * np.sqrt(result_gaia_vizier_dr3[0]['o_Gmag'][iline3]
                                  ) / result_gaia_vizier_dr3[0]['FG'][iline3]

        except IndexError:
            result_gaia_vizier_dr3 = Table(
                [[-1], [-1], [-1], [-1], [-1], [-1], [-1], [-1], [-1], [-1]],
                names=('Plx', 'e_Plx', 'Gmag', 'e_Gmag', 'FG', 'e_FG', 'RPmag',
                       'e_RPmag', 'BPmag', 'e_BPmag'))
            return result_gaia_vizier_dr3, -1
    print("Iline2:", iline3)
    print(result_gaia_vizier_dr3)
    return result_gaia_vizier_dr3[0][iline3], std_g_flux_norm1_v
Exemple #4
0
def get_parallax(objectname, radius=5):

    v_gaia = Vizier(columns=[
        "Plx", "e_Plx", '+_r', 'Gmag', 'nueff', 'pscol', 'ELAT', 'Solved'
    ])

    data = v_gaia.query_object(objectname,
                               catalog=['I/350/gaiaedr3'],
                               radius=radius * u.arcsec)

    if len(data) == 0:
        return None, None

    data = data['I/350/gaiaedr3'][0]

    plx, plx_e = data['Plx'], data['e_Plx']

    if not data['pscol']:
        data['pscol'] = 0

    # apply parallax zero point correction of Lindgren. If not possible, use the average offset.
    # https://arxiv.org/pdf/2012.01742.pdf
    # https://gitlab.com/icc-ub/public/gaiadr3_zeropoint
    try:
        zp = zpt.get_zpt(data['Gmag'], data['nueff'], data['pscol'],
                         data['ELAT'], data['Solved'])
    except Exception:
        warnings.warn(
            "Could not calculate the parallax zero point offset based on Lindgren+2020, using average"
        )
        zp = 0.02
    plx -= zp

    return plx, plx_e
Exemple #5
0
    def get_inclination(self):
        """
        This function ...
        :return:
        """

        # Inform the user
        log.info(
            "Querying the catalog of radial profiles for 161 face-on spirals ..."
        )

        # The Vizier querying object
        vizier = Vizier()
        vizier.ROW_LIMIT = -1

        # Radial profiles for 161 face-on spirals (Munoz-Mateos+, 2007)
        radial_profiles_result = vizier.query_object(self.galaxy_name,
                                                     catalog="J/ApJ/658/1006")

        # Catalog doesnt contain data for a lot of galaxies
        # If it doesnt, use DustPedia galaxy info as a backup solution
        if len(radial_profiles_result) == 0 or len(
                radial_profiles_result[0]) == 0:

            inclination = Angle(self.info["Inclination"][0], "deg")

        # We have a table and it is not empty
        else:

            table = radial_profiles_result[0]
            # distance = float(table[0]["Dist"])
            inclination = Angle(float(table[0]["i"]), "deg")

        # Set the inclination
        self.properties.inclination = inclination
def get_hip(name):
    """
    Given a star's Bayer designation, queries
    VizieR and attempts to locate a Hipparcos
    star ID at the location.

    Returns an integer HIP ID if found, or None otherwise

    Maintains a .hip_cache_stars file to speed up lookups;
    you can delete the .hip_cache_stars file to perform
    fresh lookups.
    """
    # Search the Hipparcos catalog, and only return results that include
    # a HIP (Hipparcos) column, sorting the results by magnitude.  The
    # top result is almost certainly the star we want.
    v = Vizier(catalog='I/239/hip_main', columns=["HIP", "+Vmag"])

    try:
        result = v.query_object(name)
    except EOFError:
        # if we get no results we might get an EOFError
        return None
    try:
        table = result['I/239/hip_main']
    except TypeError:
        # A TypeError means that the results didn't include anything from
        # the I/239/hip_main catalog.  The "in" operator doesn't seem to
        # work with Table objects.
        return None
    else:
        return table['HIP'][0]
Exemple #7
0
def get_hip(name):
    """
    Given a star's Bayer designation, queries
    VizieR and attempts to locate a Hipparcos
    star ID at the location.

    Returns an integer HIP ID if found, or None otherwise

    Maintains a .hip_cache_stars file to speed up lookups;
    you can delete the .hip_cache_stars file to perform
    fresh lookups.
    """
    # Search the Hipparcos catalog, and only return results that include
    # a HIP (Hipparcos) column, sorting the results by magnitude.  The
    # top result is almost certainly the star we want.
    v = Vizier(catalog='I/239/hip_main', columns=["HIP", "+Vmag"])

    try:
        result = v.query_object(name)
    except EOFError:
        # if we get no results we might get an EOFError
        return None
    try:
        table = result['I/239/hip_main']
    except TypeError:
        # A TypeError means that the results didn't include anything from
        # the I/239/hip_main catalog.  The "in" operator doesn't seem to
        # work with Table objects.
        return None
    else:
        return table['HIP'][0]
Exemple #8
0
def request_vizier(hd_identifiers):

    v = Vizier(columns=['Vmag', 'b-y', 'm1', 'c1', 'Beta'],
               catalog="II/215/catalog")
    big_table_hd = Table(names=('Vmag', 'b-y', 'm1', 'c1', 'Beta'))

    for hd_object in hd_identifiers:
        print(str(hd_object))
        hd_object = str(hd_object).strip()

        if not hd_object or hd_object == "NaN":  #If string is empty  then:  or hd_object== 'NaN'
            big_table_hd.add_row([np.nan, np.nan, np.nan, np.nan, np.nan])
        else:
            try:
                result = v.query_object(hd_object)[0][0]

            except IndexError:  # This handles if HD identifier does not exist in the catalog
                big_table_hd.add_row([np.nan, np.nan, np.nan, np.nan, np.nan])
            else:

                big_table_hd = vstack([big_table_hd, result],
                                      join_type='outer',
                                      metadata_conflicts='silent')

    df_0 = big_table_hd.to_pandas()

    return df_0
def get_ucac4_id_as_dataframe(object_name):
    result = Vizier.query_object(object_name, catalog=['I/322A'])
    if len(result) == 0:
        logging.warning(f"Returned result from vizier for object {object_name} is empty.")
    df = result[0].to_pandas()
    df2 = df.loc[df['UCAC4'] == object_name.split()[1].encode('UTF-8')]
    return df2
Exemple #10
0
def get_wcs(pattern):
    for filename in pattern:

        which_hdu = choose_hdu(filename)
        header = fits.getheader(filename, which_hdu)

        data = fits.getdata(filename, ext=0)
        threshold = detect_threshold(data, nsigma=2.)
        sigma = 3.0 * gaussian_fwhm_to_sigma  # FWHM = 3.
        kernel = Gaussian2DKernel(sigma, x_size=3, y_size=3)
        kernel.normalize()
        mean, median, std = sigma_clipped_stats(data, sigma=3)
        daofind = DAOStarFinder(fwhm=3.0, threshold=5. * std)
        sources = daofind(data - median)
        for col in sources.colnames:
            sources[col].info.format = '%.8g'  # for consistent table output

        x = np.array(sources['xcentroid'])
        y = np.array(sources['ycentroid'])
        OB = header['OBJECT']
        cobj = SkyCoord.from_name(OB, parse=True)
        cobjx = cobj.ra.degree
        cobjy = cobj.dec.degree
        refx = x[1]
        refy = y[1]

        Bin = float(header['CCDSUM'][0])
        Res = 0.25  #arcsec/px
        f_arcsec = Bin * Res  #arcsec/px
        f = f_arcsec / 3600

        for n in x:
            dx = x - refx
            xscal = cobjx + (f * dx)

        for n in y:
            dy = y - refy
            yscal = cobjy + (f * dy)

        fig1, ax = plt.subplots()
        c1 = SkyCoord(ra=xscal * u.degree,
                      dec=yscal * u.degree)  # ra and dec of each source
        ax.plot(xscal, yscal, 'ok', ms=5)

        result = Vizier.query_object(OB)
        interesting_table = result['I/305/out']
        x1 = np.array(interesting_table['RAJ2000'])
        y1 = np.array(interesting_table['DEJ2000'])
        c2 = SkyCoord(ra=x1 * u.degree, dec=y1 * u.degree)
        fig2, ax = plt.subplots()
        ax.plot(x1, y1, 'or', mfc='none', ms=10)

        idx, d2d, d3d = c1.match_to_catalog_sky(c2)
        fig3, ax = plt.subplots()
        ax.plot(xscal, yscal, 'ok', ms=5)
        ax.plot(x1, y1, 'or', mfc='none', ms=10)
        plt.show()

        dx, d2d, d3d = c1.match_to_catalog_sky(c2)
Exemple #11
0
def showVizierCatalogs(name):
    from astroquery.vizier import Vizier
    Vizier.ROW_LIMIT = 50
    from astropy import coordinates
    from astropy import units as u
    c = coordinates.SkyCoord(ra, dec, unit=('deg', 'deg'), frame='icrs')
    results = Vizier.query_object(name)
    return results
Exemple #12
0
def vizierLocationForTarget(exp, target, doMotionCorrection):
    """Get the target location from Vizier optionally correction motion.

    Parameters
    ----------
    target : `str`
        The name of the target, e.g. 'HD 55852'

    Returns
    -------
    targetLocation : `lsst.geom.SpherePoint` or `None`
        Location of the target object, optionally corrected for
        proper motion and parallax.

    Raises
    ------
    ValueError
        If object not found in Hipparcos2 via Vizier.
        This is quite common, even for bright objects.
    """
    # do not import at the module level - tests crash due to a race
    # condition with directory creation
    from astroquery.vizier import Vizier

    result = Vizier.query_object(
        target)  # result is an empty table list for an unknown target
    try:
        star = result['I/311/hip2']
    except TypeError:  # if 'I/311/hip2' not in result (result doesn't allow easy checking without a try)
        raise ValueError

    epoch = "J1991.25"
    coord = SkyCoord(
        ra=star[0]['RArad'] * u.Unit(star['RArad'].unit),
        dec=star[0]['DErad'] * u.Unit(star['DErad'].unit),
        obstime=epoch,
        pm_ra_cosdec=star[0]['pmRA'] *
        u.Unit(star['pmRA'].unit),  # NB contains cosdec already
        pm_dec=star[0]['pmDE'] * u.Unit(star['pmDE'].unit),
        distance=Distance(parallax=star[0]['Plx'] * u.Unit(star['Plx'].unit)))

    if doMotionCorrection:
        expDate = exp.getInfo().getVisitInfo().getDate()
        obsTime = astropy.time.Time(expDate.get(expDate.EPOCH),
                                    format='jyear',
                                    scale='tai')
        newCoord = coord.apply_space_motion(new_obstime=obsTime)
    else:
        newCoord = coord

    raRad, decRad = newCoord.ra.rad, newCoord.dec.rad
    ra = geom.Angle(raRad)
    dec = geom.Angle(decRad)
    targetLocation = geom.SpherePoint(ra, dec)
    return targetLocation
Exemple #13
0
    def test_query_target_error(self):
        jwst = JwstClass(show_messages=False)
        simbad = Simbad()
        ned = Ned()
        vizier = Vizier()
        # Testing default parameters
        with pytest.raises(ValueError) as err:
            jwst.query_target(target_name="M1", target_resolver="")
        assert "This target resolver is not allowed" in err.value.args[0]
        with pytest.raises(ValueError) as err:
            jwst.query_target("TEST")
        assert "This target name cannot be determined with this resolver: ALL" in err.value.args[
            0]
        with pytest.raises(ValueError) as err:
            jwst.query_target(target_name="M1", target_resolver="ALL")
        assert err.value.args[0] in [
            f"This target name cannot be determined "
            f"with this resolver: ALL", "Missing "
            f"required argument: 'width'"
        ]

        # Testing no valid coordinates from resolvers
        simbad_file = data_path(
            'test_query_by_target_name_simbad_ned_error.vot')
        simbad_table = Table.read(simbad_file)
        simbad.query_object = MagicMock(return_value=simbad_table)
        ned_file = data_path('test_query_by_target_name_simbad_ned_error.vot')
        ned_table = Table.read(ned_file)
        ned.query_object = MagicMock(return_value=ned_table)
        vizier_file = data_path('test_query_by_target_name_vizier_error.vot')
        vizier_table = Table.read(vizier_file)
        vizier.query_object = MagicMock(return_value=vizier_table)

        # coordinate_error = 'coordinate must be either a string or astropy.coordinates'
        with pytest.raises(ValueError) as err:
            jwst.query_target(target_name="test",
                              target_resolver="SIMBAD",
                              radius=units.Quantity(5, units.deg))
        assert 'This target name cannot be determined with this resolver: SIMBAD' in err.value.args[
            0]

        with pytest.raises(ValueError) as err:
            jwst.query_target(target_name="test",
                              target_resolver="NED",
                              radius=units.Quantity(5, units.deg))
        assert 'This target name cannot be determined with this resolver: NED' in err.value.args[
            0]

        with pytest.raises(ValueError) as err:
            jwst.query_target(target_name="test",
                              target_resolver="VIZIER",
                              radius=units.Quantity(5, units.deg))
        assert 'This target name cannot be determined with this resolver: VIZIER' in err.value.args[
            0]
Exemple #14
0
def getVizierResults(name, radius):
    from astroquery.vizier import Vizier
    from astropy import units as u
    v = Vizier(columns=["all"], catalog='I/345/gaia2')
    v.ROW_LIMIT = 25000
    results = v.query_object(name,
                             catalog='I/345/gaia2',
                             radius=radius * u.arcsec)
    keys = results[0].keys()
    objectList = gaiaClass.GAIAObjects(gaiaTable=results[0])

    return objectList
Exemple #15
0
    def get_s4g_properties(self):
        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Querying the S4G catalog ...")

        # The Vizier querying object
        vizier = Vizier(columns=[
            'Name', 'RAJ2000', 'DEJ2000', 'amaj', 'ell', 'Dmean', "e_Dmean",
            "PA"
        ])
        vizier.ROW_LIMIT = -1

        # Get parameters from S4G catalog
        result = vizier.query_object(self.galaxy_name,
                                     catalog=["J/PASP/122/1397/s4g"])
        table = result[0]

        # Galaxy name for S4G catalog
        self.properties.name = table["Name"][0]

        # Galaxy center from decomposition (?)
        ra_center = table["RAJ2000"][0]
        dec_center = table["DEJ2000"][0]
        center = SkyCoordinate(ra=ra_center,
                               dec=dec_center,
                               unit="deg",
                               frame='fk5')
        self.properties.center = center

        # Center position
        #self.properties.center = SkyCoordinate(ra=self.info["RA"][0], dec=self.info["DEC"][0], unit="deg") # center position from DustPedia

        # Distance
        self.properties.distance = table["Dmean"][0] * u("Mpc")
        self.properties.distance_error = table["e_Dmean"][0] * u("Mpc")

        # Major axis, ellipticity, position angle
        self.properties.major_arcsec = table["amaj"][0] * u("arcsec")
        self.properties.major = (self.properties.distance *
                                 self.properties.major_arcsec).to(
                                     "pc",
                                     equivalencies=dimensionless_angles())

        # Ellipticity
        self.properties.ellipticity = table["ell"][0]
        self.properties.position_angle = Angle(table["PA"][0] + 90.0, u("deg"))
Exemple #16
0
def get_galaxy_s4g_one_component_info(name):

    """
    This function ...
    :param name:
    :return:
    """

    # The Vizier querying object
    vizier = Vizier()
    vizier.ROW_LIMIT = -1

    # Get the "galaxies" table
    result = vizier.query_object(name, catalog=["J/ApJS/219/4/galaxies"])

    # No results?
    if len(result) == 0: return None, None, None, None, None, None

    # Get table
    table = result[0]

    # PA: [0.2/180] Outer isophote position angle
    # e_PA: [0/63] Standard deviation in PA
    # Ell:  [0.008/1] Outer isophote ellipticity
    # e_Ell: [0/0.3] Standard deviation in Ell

    # PA1: Elliptical isophote position angle in deg
    # n: Sersic index
    # Re: effective radius in arcsec

    # Tmag: total magnitude

    s4g_name = table["Name"][0]

    pa = Angle(table["PA"][0] - 90., "deg")
    pa_error = Angle(table["e_PA"][0], "deg")

    ellipticity = table["Ell"][0]
    ellipticity_error = table["e_Ell"][0]

    n = table["n"][0]

    re = table["Re"][0] * u("arcsec")

    mag = table["Tmag"][0]

    # Return the results
    return s4g_name, pa, ellipticity, n, re, mag
Exemple #17
0
def strom_phot(star):
    from astroquery.vizier import Vizier
    import numpy as np
    import os

    EP2015_stromgren = Vizier(catalog="J/A+A/580/A23")

    result = EP2015_stromgren.query_object(star)

    #print result

    if not result:
        return 0

    #print result[0].keys()
    by = result[0]["b-y"][0]
    c1 = result[0]["c1"][0]
    m1 = result[0]["m1"][0]
    beta = result[0]["beta"][0]

    if not beta:
        return 0

    if (2.72 < beta < 2.88) & (0.05 < by < 0.22):
        n = 6
    elif (2.87 < beta < 2.93) & (-0.01 < by < 0.06):
        n = 5
    elif (2.60 < beta < 2.72) & (0.22 < by < 0.39):
        n = 7
    elif (0.02 < m1 < 0.76) & (0.39 < by < 1.00):
        n = 8
    else:
        n = 6

    with open("starParam.pro", "wb") as idl_code:
        idl_code.write("uvbybeta," + str(by) + ',' + str(m1) + ',' + str(c1) +
                       ',' + str(beta) + ',' + str(n) + ',Teff\n')
        idl_code.write("print,'readline: ',Teff")

    os.system("nohup nice $IDL_DIR/bin/idl < starParam.pro > idlout.txt")

    idl_output = open("idlout.txt", "rb")

    out = idl_output.readlines()
    temp = np.float(out[-1][9:])

    return temp
Exemple #18
0
def getUniqueVizierResult(name, radius):
    from astroquery.vizier import Vizier
    from astropy import units as u
    v = Vizier(columns=["all"], catalog='I/345/gaia2')
    v.ROW_LIMIT = 25000
    results = v.query_object(name, catalog='I/345/gaia2')
    # results = v.query_object(name, catalog='I/345/gaia2', radius = radius * u.arcsec)
    keys = results[0].keys()
    results[0].pprint()
    print("Number of results: %d" % len(results[0]))
    raStr, decStr = getSimbadCoordinates(name)
    targetRA, targetDEC = parseCoords(raStr, decStr)
    print("Location of %s is %f, %f" % (name, targetRA, targetDEC))
    objectList = gaiaClass.GAIAObjects(gaiaTable=results[0])
    closestMatch = objectList.calcAngularDistance(targetRA, targetDEC)
    singleResult = objectList.getObjectByDR2Name(closestMatch)
    return keys, singleResult
Exemple #19
0
def query_catalog_for_object(identifier, catalog=duncan1991):
    """
    Parameters
    ----------
    identifier : str

    catalog : str (optional)

    Returns
    -------

    """
    query = Vizier.query_object(identifier, catalog=catalog)

    if len(query) > 0:
        return query[0][0]
    else:
        return dict(Smean=np.nan, Smin=np.nan, Smax=np.nan)
Exemple #20
0
def query_catalog_for_object(identifier, catalog=duncan1991):
    """
    Parameters
    ----------
    identifier : str

    catalog : str (optional)

    Returns
    -------

    """
    query = Vizier.query_object(identifier, catalog=catalog)

    if len(query) > 0:
        return query[0][0]
    else:
        return dict(Smean=np.nan, Smin=np.nan, Smax=np.nan)
Exemple #21
0
def create_vizier_metafile():
    result = Vizier.query_object("sirius")

    text_file = open("D:\\Programming\\Astronomy\\VizierMeta.txt", "w")
    text_file.truncate()

    for table in result:
        #print table.colnames
        text_file.write('(' + table.meta['name'] + ') ' + table.meta['description'] + '\n')

        for col in table.colnames:
            text_file.write('\t' + col + '\n')

        text_file.write('\n')
        text_file.write('\n')
        #print table.colnames
        #print table.meta['description']

    text_file.close()
Exemple #22
0
def get_galaxy_s4g_one_component_info(name):

    """
    This function ...
    :param name:
    :return:
    """

    # The Vizier querying object
    vizier = Vizier()
    vizier.ROW_LIMIT = -1

    # Get the "galaxies" table
    result = vizier.query_object(name, catalog=["J/ApJS/219/4/galaxies"])
    table = result[0]

    # PA: [0.2/180] Outer isophote position angle
    # e_PA: [0/63] Standard deviation in PA
    # Ell:  [0.008/1] Outer isophote ellipticity
    # e_Ell: [0/0.3] Standard deviation in Ell

    # PA1: Elliptical isophote position angle in deg
    # n: Sersic index
    # Re: effective radius in arcsec

    # Tmag: total magnitude

    s4g_name = table["Name"][0]

    pa = Angle(table["PA"][0] - 90., "deg")
    pa_error = Angle(table["e_PA"][0], "deg")

    ellipticity = table["Ell"][0]
    ellipticity_error = table["e_Ell"][0]

    n = table["n"][0]

    re = table["Re"][0] * u("arcsec")

    mag = table["Tmag"][0]

    # Return the results
    return s4g_name, pa, ellipticity, n, re, mag
Exemple #23
0
    def get_s4g_properties(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Querying the S4G catalog ...")

        # The Vizier querying object
        vizier = Vizier(columns=['Name', 'RAJ2000', 'DEJ2000', 'amaj', 'ell', 'Dmean', "e_Dmean", "PA"])
        vizier.ROW_LIMIT = -1

        # Get parameters from S4G catalog
        result = vizier.query_object(self.galaxy_name, catalog=["J/PASP/122/1397/s4g"])
        table = result[0]

        # Galaxy name for S4G catalog
        self.properties.name = table["Name"][0]

        # Galaxy center from decomposition (?)
        ra_center = table["RAJ2000"][0]
        dec_center = table["DEJ2000"][0]
        center = SkyCoordinate(ra=ra_center, dec=dec_center, unit="deg", frame='fk5')
        self.properties.center = center

        # Center position
        #self.properties.center = SkyCoordinate(ra=self.info["RA"][0], dec=self.info["DEC"][0], unit="deg") # center position from DustPedia

        # Distance
        self.properties.distance = table["Dmean"][0] * u("Mpc")
        self.properties.distance_error = table["e_Dmean"][0] * u("Mpc")

        # Major axis, ellipticity, position angle
        self.properties.major_arcsec = table["amaj"][0] * u("arcsec")
        self.properties.major = (self.properties.distance * self.properties.major_arcsec).to("pc", equivalencies=dimensionless_angles())

        # Ellipticity
        self.properties.ellipticity = table["ell"][0]
        self.properties.position_angle = Angle(table["PA"][0] + 90.0, u("deg"))
Exemple #24
0
    def query_objects(self, names: TList[str]) -> TList[CatalogSource]:
        """
        Return a list of VizieR catalog objects with the specified names

        :param names: object names

        :return: list of catalog objects with the given names
        """
        kwargs = {}
        if self.vizier_server:
            kwargs['vizier_server'] = self.vizier_server
        viz = Vizier(
            catalog=self.vizier_catalog, columns=self._columns,
            row_limit=len(names), **kwargs)
        rows = []
        for name in names:
            resp = viz.query_object(
                name, catalog=viz.catalog, cache=self.cache)
            if resp:
                rows.append(resp[0][0])
        return self.table_to_sources(rows)
Exemple #25
0
def query():
    result = Vizier.query_object("Betelgeuse", catalog=["V/137D/XHIP", "V/85A/catalog"])
    #result = Vizier.query_object("sirius", catalog="J/A+A/501/949/table")

    text_file = open("D:\\Programming\\Astronomy\\VizierTest.txt", "w")
    text_file.truncate()

    for table in result:
        text_file.write('\n')
        text_file.write('\n')
        text_file.write('(' + table.meta['name'] + ') ' + table.meta['description'] + '\n')
        text_file.write('\n')
        text_file.write('\n')
        for row in table:
            for col in table.colnames:
                text_file.write(col + '\t' + str(row[col]) + '\n')
                text_file.write('\n')
                #print row[col]

            #print row

    text_file.close()
Exemple #26
0
 def query_KIC(self, ID = None, radius = 10.0*u.arcsec):
 
     warnings.filterwarnings("ignore", category = MergeConflictWarning)
 
     if ID is None:
         ID = self.IDs['KIC']
 
     tbl = Table(names = ('KIC',), dtype = (int,))
     for i, id in tqdm(enumerate(ID)):
         
         if not isinstance(id, ):
             tbl.add_row(None)
             tbl[-1]['KIC'] = int(re.sub(r"\D", "", id))
             
         else:
             job = Vizier.query_object(object_name = id, catalog = 'V/133/kic', radius = radius)
             
             if len(job) > 0:
                 
                 id_int = int(id.replace('KIC',''))
             
                 idx = job[0]['KIC'] == id_int
                 
                 id_int = int(id.replace('KIC',''))
             
                 idx = job[0]['KIC'] == id_int
                 
                 tbl = avstack([tbl, job[0][idx]]) 
             
             else:
                 tbl.add_row(None)
                 tbl[-1]['KIC'] = int(re.sub(r"\D", "", id))
                 warnings.warn(f'Unable to find KIC entry for {id}')
 
     self.KIC = tbl
     return self.KIC
E1DecRange = []
# Loop through each Landolt star and retrieve the USNO-B1 data for that object.
for iStar, star in enumerate(landoltStars):
    # Parse the star's name in SimbadName
    simbadName = star['SimbadName'].decode('utf-8')

    # Parse this star's pointing
    RA, Dec  = star['_RAJ2000'], star['_DEJ2000']

    # Skip over Landolt stars more than 10 degrees from the equator.
    if np.abs(Dec) > 20.0:
        print('Star {0} is far from the equator... skipping'.format(simbadName))
        continue

    # Query the USNO-B1.0 data for this object
    USNOB = Vizier.query_object(simbadName,
        radius = 5.0*u.arcsec, catalog='USNO-B1')

    if len(USNOB) > 0:
        # Some match was found, so grab that 'catalog'
        USNOB = USNOB[0]
    else:
        print('Star {0} not matched in USNO-B1.0... skipping'.format(simbadName))
        continue

    # If there is more than one object returned,
    if len(USNOB) > 1:
        # Then find the object closest to the query point and just use that.
        matchInd = np.where(USNOB['_r'].data == USNOB['_r'].data.min())
        USNOB    = USNOB[matchInd]

    # Test if we know where this data came from
Exemple #28
0
def get_galaxy_info(name, position):
    """
    This function ...
    :param name:
    :param position:
    :return:
    """

    # Obtain more information about this galaxy
    try:

        ned_result = Ned.query_object(name)
        ned_entry = ned_result[0]

        # Get a more common name for this galaxy (sometimes, the name obtained from NED is one starting with 2MASX .., use the PGC name in this case)
        if ned_entry["Object Name"].startswith("2MASX "): gal_name = name
        else: gal_name = ned_entry["Object Name"]

        # Get the redshift
        gal_redshift = ned_entry["Redshift"]
        if isinstance(gal_redshift, np.ma.core.MaskedConstant):
            gal_redshift = None

        # Get the type (G=galaxy, HII ...)
        gal_type = ned_entry["Type"]
        if isinstance(gal_type, np.ma.core.MaskedConstant): gal_type = None

    except astroquery.exceptions.RemoteServiceError:

        # Set attributes
        gal_name = name
        gal_redshift = None
        gal_type = None

    except astroquery.exceptions.TimeoutError:

        # Set attributes
        gal_name = name
        gal_redshift = None
        gal_type = None

    except:

        # Set attributes
        gal_name = name
        gal_redshift = None
        gal_type = None

    # Create a new Vizier object and set the row limit to -1 (unlimited)
    viz = Vizier(keywords=["galaxies", "optical"])
    viz.ROW_LIMIT = -1

    # Query Vizier and obtain the resulting table
    result = viz.query_object(name.replace(" ", ""), catalog=["VII/237"])

    # Not found ... TODO: fix this ... this object was in the first query output
    if len(result) == 0:
        return name, position, None, None, [], None, None, None, None, None, None

    table = result[0]

    # Get the correct entry (sometimes, for example for mergers, querying with the name of one galaxy gives two hits! We have to obtain the right one each time!)
    if len(table) == 0:
        raise ValueError("The galaxy could not be found under this name")
    elif len(table) == 1:
        entry = table[0]
    else:

        entry = None

        # Some rows don't have names, if no match is found based on the name just take the row that has other names defined
        rows_with_names = []
        for row in table:
            if row["ANames"]: rows_with_names.append(row)

        # If only one row remains, take that one for the galaxy we are looking for
        if len(rows_with_names) == 1:
            entry = rows_with_names[0]

            # Else, loop over the rows where names are defined and look for a match
        else:
            for row in rows_with_names:

                names = row["ANames"]

                if name.replace(" ", "") in names or gal_name.replace(
                        " ", "") in names:

                    entry = row
                    break

        # If no matches are found, look for the table entry for which the coordinate matches the given position (if any)
        if entry is None and position is not None:
            for row in table:
                if ('_RAJ2000' in row.colnames) and ('_DEJ2000'
                                                     in row.colnames):
                    ra_key, dec_key = '_RAJ2000', '_DEJ2000'
                elif ('RAJ2000' in row.colnames) and ('DEJ2000'
                                                      in row.colnames):
                    ra_key, dec_key = 'RAJ2000', 'DEJ2000'
                if np.isclose(
                        astropy.coordinates.Angle(row[ra_key] + ' hours').deg,
                        position.ra.value) and np.isclose(
                            astropy.coordinates.Angle(row[dec_key] +
                                                      ' degrees').deg,
                            position.dec.value):
                    entry = row
                    break

    # Note: another temporary fix
    if entry is None:
        return name, position, None, None, [], None, None, None, None, None, None

    # Get the right ascension and the declination
    if ('_RAJ2000' in entry.colnames) and ('_DEJ2000' in entry.colnames):
        ra_key, dec_key = '_RAJ2000', '_DEJ2000'
    elif ('RAJ2000' in entry.colnames) and ('DEJ2000' in entry.colnames):
        ra_key, dec_key = 'RAJ2000', 'DEJ2000'
    position = SkyCoordinate(ra=entry[ra_key],
                             dec=entry[dec_key],
                             unit="deg",
                             frame="fk5")

    # Get the names given to this galaxy
    gal_names = entry["ANames"].split() if entry["ANames"] else []

    # Get the size of the galaxy
    ratio = np.power(10.0, entry["logR25"]) if entry["logR25"] else None
    diameter = np.power(10.0, entry["logD25"]) * 0.1 * Unit(
        "arcmin") if entry["logD25"] else None

    #print("  D25_diameter = ", diameter)

    radial_profiles_result = viz.query_object(name, catalog="J/ApJ/658/1006")

    if len(radial_profiles_result) > 0:

        radial_profiles_entry = radial_profiles_result[0][0]

        gal_distance = radial_profiles_entry["Dist"] * Unit("Mpc")
        gal_inclination = Angle(radial_profiles_entry["i"], "deg")
        gal_d25 = radial_profiles_entry["D25"] * Unit("arcmin")

    else:

        gal_distance = None
        gal_inclination = None
        gal_d25 = None

    # Get the size of major and minor axes
    gal_major = diameter
    gal_minor = diameter / ratio if diameter is not None and ratio is not None else None

    # Get the position angle of the galaxy
    gal_pa = Angle(entry["PA"] - 90.0, "deg") if entry["PA"] else None

    # Create and return a new Galaxy instance
    return gal_name, position, gal_redshift, gal_type, gal_names, gal_distance, gal_inclination, gal_d25, gal_major, gal_minor, gal_pa
Exemple #29
0
class S4G(Configurable):
    """
    This class ...
    """
    def __init__(self, *args, **kwargs):
        """
        The constructor ...
        :param kwargs:
        """

        # Call the constructor of the base class
        super(S4G, self).__init__(*args, **kwargs)

        # Names
        self.ngc_name = None
        self.ngc_name_nospaces = None

        # The Vizier querying object, specifying the necessary columns for this class
        self.vizier = Vizier(columns=[
            'Name', 'RAJ2000', 'DEJ2000', 'Dmean', 'e_Dmean', 'amaj', 'ell',
            '[3.6]', '[4.5]', 'e_[3.6]', 'e_[4.5]', 'PA'
        ])
        self.vizier.ROW_LIMIT = -1

        # The DustPedia database
        #self.database = DustPediaDatabase()

        # The path for the S4G decomposition models
        #self.components_2d_s4g_path = None

        # The galaxy properties object
        self.properties = None

        # The dictionary of components
        self.components = dict()

        #
        self.disk_pa = None
        self.disk = None
        self.bulge = None

    # -----------------------------------------------------------------

    def run(self, **kwargs):
        """
        This function ...
        :return:
        """

        # 1. Call the setup function
        self.setup(**kwargs)

        # 2. Get galaxy properties
        self.get_properties()

        # Tracing spiral density waves in M81: (Kendall et. al, 2008)
        # - Sersic bulge:
        #    n = 2.62
        #    Re = 46.2 arcsec
        #    b/a = 0.71
        #    PA = −31.9°
        # - Exponential disk:
        #    Rs = 155.4 arcsec
        #    b/a = 0.52
        #    PA = −28.3°

        # self.get_p4()  # currently (writing on the 31th of march 2016) there is a problem with the effective radius values

        # (at least for M81) on Vizier as well as in the PDF version of table 7 (S4G models homepage).

        # 3. Parse the S4G table 8 to get the decomposition parameters
        self.get_parameters_from_table()

        # 4. Show
        if self.config.show: self.show()

        # 5. Writing
        if self.config.output is not None: self.write()

    # -----------------------------------------------------------------

    def setup(self, **kwargs):
        """
        This function ...
        :param kwargs:
        :return:
        """

        # Call the setup function of the base class
        super(S4G, self).setup(**kwargs)

        # Create the galaxy properties object
        self.properties = GalaxyProperties()
        self.properties.name = self.config.galaxy_name

        # Set the path
        #self.components_2d_s4g_path = fs.create_directory_in(self.components_2d_path, "S4G")

    # -----------------------------------------------------------------

    def get_properties(self):
        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Getting the galaxy properties ...")

        # Name
        self.get_ngc_name()

        #self.get_dustpedia_info()

        # S4G
        self.get_s4g_properties()

        #self.get_inclination()

        # To first order, cos i = b/a where a & b
        # are the observed major and minor
        # axes (assume disk is intrinsically
        # circular)

        # But this implies galaxies have zero
        # thickness at i = 90°! Better to assume
        # that spirals are oblate ellipsoids with
        # intrinsic axis ratios a:a:c. If q = c/a,
        # then after a bit a simple geometry

        # cos^2(i) = [ (b/a)^2 - q^2 ] / (1-q^2)

        # The best fit to observed spirals yields
        # q = 0.13 for Sc galaxies

        # Ellipticity (1-b/a)

        b_to_a = 1. - self.properties.ellipticity

        # Calculate the inclination
        inclination_deg = 90. - math.degrees(math.acos(b_to_a))
        inclination = Angle(inclination_deg, "deg")
        self.properties.inclination = inclination

    # -----------------------------------------------------------------

    def get_ngc_name(self):
        """
        This function ...
        :return:
        """

        # Get the NGC name of the galaxy
        self.properties.ngc_name = catalogs.get_ngc_name(
            self.config.galaxy_name)

        # Set ...
        self.ngc_name = self.properties.ngc_name
        self.ngc_name_nospaces = self.ngc_name.replace(" ", "")

    # -----------------------------------------------------------------

    def get_dustpedia_info(self):
        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Fetching galaxy info from the DustPedia database ...")

        # Get the info
        self.info = self.database.get_galaxy_info(self.ngc_name_nospaces)

    # -----------------------------------------------------------------

    def get_s4g_properties(self):
        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Querying the S4G catalog ...")

        # Get parameters from S4G catalog
        result = self.vizier.query_object(self.config.galaxy_name,
                                          catalog=["J/PASP/122/1397/s4g"])
        table = result[0]

        # Galaxy name for S4G catalog
        self.properties.name = table["Name"][0]
        # Galaxy center from decomposition (?)
        ra_center = table["RAJ2000"][0]
        dec_center = table["DEJ2000"][0]
        center = SkyCoordinate(ra=ra_center,
                               dec=dec_center,
                               unit="deg",
                               frame='fk5')
        self.properties.center = center

        # Center position
        #self.properties.center = SkyCoordinate(ra=self.info["RA"][0], dec=self.info["DEC"][0], unit="deg") # center position from DustPedia

        # Distance
        self.properties.distance = table["Dmean"][0] * u("Mpc")
        self.properties.distance_error = table["e_Dmean"][0] * u("Mpc")

        # Major axis, ellipticity, position angle
        self.properties.major_arcsec = table["amaj"][0] * u("arcsec")
        self.properties.major = (self.properties.distance *
                                 self.properties.major_arcsec).to(
                                     "pc",
                                     equivalencies=dimensionless_angles())

        # Ellipticity
        self.properties.ellipticity = table["ell"][0]
        self.properties.position_angle = Angle(table["PA"][0] + 90.0, u("deg"))

        # Magnitudes
        asymptotic_ab_magnitude_i1 = table["__3.6_"][0]
        asymptotic_ab_magnitude_i2 = table["__4.5_"][0]
        asymptotic_ab_magnitude_i1_error = table["e__3.6_"][0]
        asymptotic_ab_magnitude_i2_error = table["e__4.5_"][0]

        #self.properties.i1_mag = asymptotic_ab_magnitude_i1
        #self.properties.i1_mag_error = asymptotic_ab_magnitude_i1_error
        #self.properties.i2_mag = asymptotic_ab_magnitude_i2
        #self.properties.i2_mag_error = asymptotic_ab_magnitude_i2_error

        #self.properties.i1_fluxdensity = unitconversion.ab_to_jansky(self.properties.i1_mag) * Unit("Jy")
        #i1_fluxdensity_lower = unitconversion.ab_to_jansky(
        #    self.properties.i1_mag + self.properties.i1_mag_error) * Unit("Jy")
        #i1_fluxdensity_upper = unitconversion.ab_to_jansky(
        #    self.properties.i1_mag - self.properties.i1_mag_error) * Unit("Jy")
        #i1_error = ErrorBar(i1_fluxdensity_lower, i1_fluxdensity_upper, at=self.properties.i1_fluxdensity)
        #self.properties.i1_error = i1_error.average

        #self.properties.i2_fluxdensity = unitconversion.ab_to_jansky(self.properties.i2_mag) * Unit("Jy")
        #i2_fluxdensity_lower = unitconversion.ab_to_jansky(
        #    self.properties.i2_mag + self.properties.i2_mag_error) * Unit("Jy")
        #i2_fluxdensity_upper = unitconversion.ab_to_jansky(
        #    self.properties.i2_mag - self.properties.i2_mag_error) * Unit("Jy")
        #i2_error = ErrorBar(i2_fluxdensity_lower, i2_fluxdensity_upper, at=self.properties.i2_fluxdensity)
        #self.properties.i2_error = i2_error.average

        # Other ...
        # absolute_magnitude_i1 = table["M3.6"][0]
        # absolute_magnitude_i2 = table["M4.5"][0]
        # stellar_mass = 10.0**table["logM_"][0] * u.Unit("Msun")

    # -----------------------------------------------------------------

    def get_inclination(self):
        """
        This function ...
        :return:
        """

        # Inform the user
        log.info(
            "Querying the catalog of radial profiles for 161 face-on spirals ..."
        )

        # Radial profiles for 161 face-on spirals (Munoz-Mateos+, 2007)
        radial_profiles_result = self.vizier.query_object(
            self.config.galaxy_name, catalog="J/ApJ/658/1006")

        distance = float(radial_profiles_result[0][0]["Dist"])
        inclination = Angle(float(radial_profiles_result[0][0]["i"]), "deg")

        # Set the inclination
        self.properties.inclination = inclination

    # -----------------------------------------------------------------

    def get_p4(self):
        """
        This function ...
        :return:
        """

        #http://vizier.cfa.harvard.edu/viz-bin/VizieR?-source=J/ApJS/219/4

        # J/ApJS/219/4: S4G pipeline 4: multi-component decompositions (Salo+, 2015)

        #  - J/ApJS/219/4/galaxies: Parameters of the galaxies; center, outer orientation, sky background; and 1-component Sersic fits (tables 1 and 6) (2352 rows)
        #  - J/ApJS/219/4/table7: *Parameters of final multicomponent decompositions (Note) (4629 rows)

        # Inform the user
        log.info("Querying the S4G pipeline 4 catalog ...")

        # Get the "galaxies" table
        result = self.vizier.query_object(self.config.galaxy_name,
                                          catalog=["J/ApJS/219/4/galaxies"])
        table = result[0]

        # PA: [0.2/180] Outer isophote position angle
        # e_PA: [0/63] Standard deviation in PA
        # Ell:  [0.008/1] Outer isophote ellipticity
        # e_Ell: [0/0.3] Standard deviation in Ell

        pa = Angle(table["PA"][0] - 90., "deg")
        pa_error = Angle(table["e_PA"][0], "deg")

        ellipticity = table["Ell"][0]
        ellipticity_error = table["e_Ell"][0]

        # Get the "table7" table
        result = self.vizier.get_catalogs("J/ApJS/219/4/table7")
        table = result[0]

        # Name: Galaxy name
        # Mod: Type of final decomposition model
        # Nc: [1/4] Number of components in the model (1-4)
        # Q: [3/5] Quality flag, 5=most reliable
        # C: Physical interpretation of the component
        # Fn: The GALFIT function used for the component (sersic, edgedisk, expdisk, ferrer2 or psf)
        # f1: [0.006/1] "sersic" fraction of the total model flux
        # mag1:  [7/19.4] "sersic" total 3.6um AB magnitude
        # q1: [0.1/1] "sersic" axis ratio
        # PA1: [0.08/180] "sersic" position angle [deg]
        # Re: [0.004/430] "sersic" effective radius (Re) [arcsec]
        # n: [0.01/20] "sersic" parameter n
        # f2: [0.02/1] "edgedisk" fraction of the total model flux
        # mu02: [11.8/24.6] "edgedisk" central surface face-on brightness (µ0) [mag/arcsec2]
        # PA2: [-90/90] "edgedisk" PA [deg]
        # hr2: [1/153] "edgedisk" exponential scale length (hr) [arcsec]
        # hz2: [0.003/39] "edgedisk" z-scale hz [arcsec]
        # f3: [0.02/1] "expdisk" fraction of the total model flux
        # mag3: [6.5/18.1] "expdisk" total 3.6um AB magnitude [mag]
        # q3: [0.1/1] "expdisk" axis ratio
        # PA3: [-90/90] "expdisk" position angle [deg]
        # hr3: [0.7/332] "expdisk" exponential scale length (hr) [arcsec]
        # mu03: [16.4/25.3] "expdisk" central surface face-on brightness (µ0) [mag/arcsec2]
        # f4: [0.003/0.6] "ferrer2" fraction of the total model flux
        # mu04: [16/24.8] "ferrer2" central surface sky brightness (µ0) [mag/arcsec2]
        # q4: [0.01/1] "ferrer2" axis ratio
        # PA4: [-90/90] "ferrer2" position angle [deg]
        # Rbar: [3.7/232.5] "ferrer2" outer truncation radius of the bar (Rbar) [arcsec]
        # f5: [0.001/0.4] "psf" fraction of the total model flux
        # mag5: [11.5/21.1] "psf" total 3.6um AB magnitude [mag]

        indices = tables.find_indices(table, self.ngc_name_nospaces, "Name")

        labels = {
            "sersic": 1,
            "edgedisk": 2,
            "expdisk": 3,
            "ferrer2": 4,
            "psf": 5
        }

        #units = {"f": None, "mag": "mag", "q": None, "PA": "deg", }

        # Loop over the indices
        for index in indices:

            model_type = table["Mod"][index]
            number_of_components = table["Nc"][index]
            quality = table["Q"][index]
            interpretation = table["C"][index]
            functionname = table["Fn"][index]

            component_parameters = Map()

            if self.parameters.model_type is not None:
                assert model_type == self.parameters.model_type
            if self.parameters.number_of_components is not None:
                assert number_of_components == self.parameters.number_of_components
            if self.parameters.quality is not None:
                assert quality == self.parameters.quality
            self.parameters.model_type = model_type
            self.parameters.number_of_components = number_of_components
            self.parameters.quality = quality

            for key in table.colnames:

                if not key.endswith(str(labels[functionname])): continue

                parameter = key[:-1]

                value = table[key][index]

                if parameter == "PA":
                    value = Angle(value + 90., "deg")
                    if quadrant(value) == 2: value = value - Angle(180., "deg")
                    elif quadrant(value) == 3:
                        value = value + Angle(180., "deg")

                    if value.to("deg").value > 180.:
                        value = value - Angle(360., "deg")
                    elif value.to("deg").value < -180.:
                        value = value + Angle(360., "deg")
                elif parameter == "mag":
                    parameter = "fluxdensity"
                    value = unitconversion.ab_to_jansky(value) * u("Jy")
                elif parameter == "mu0":
                    value = value * u("mag/arcsec2")
                elif parameter == "hr":
                    value = value * u("arcsec")
                    value = (self.parameters.distance * value).to(
                        "pc", equivalencies=dimensionless_angles())
                elif parameter == "hz":
                    value = value * u("arcsec")
                    value = (self.parameters.distance * value).to(
                        "pc", equivalencies=dimensionless_angles())

                component_parameters[parameter] = value

            if functionname == "sersic":

                re = table["Re"][index] * u("arcsec")
                component_parameters["Re"] = (
                    self.parameters.distance * re).to(
                        "pc", equivalencies=dimensionless_angles())
                component_parameters["n"] = table["n"][index]

            elif functionname == "ferrer2":

                rbar = table["Rbar"][index] * u("arcsec")
                component_parameters["Rbar"] = (
                    self.parameters.distance * rbar).to(
                        "pc", equivalencies=dimensionless_angles())

            if interpretation == "B":  # bulge

                self.parameters.bulge = component_parameters

            elif interpretation == "D":  # disk

                self.parameters.disk = component_parameters

            else:
                raise RuntimeError("Unrecognized component: " + interpretation)

        # Determine the full path to the parameters file
        #path = fs.join(self.components_path, "parameters.dat")

        # Write the parameters to the specified location
        #write_parameters(self.parameters, path)

    # -----------------------------------------------------------------

    def get_parameters_from_table(self):
        """
        This function ...
        :return:
        """

        # Inform the user
        log.info(
            "Getting the structural galaxy parameters from the S4G catalog ..."
        )

        # Inform the user
        log.info("Parsing S4G table 8 to get the decomposition parameters ...")

        #The table columns are:
        # (1) the running number (1-2352),
        # (2) the galaxy name,
        # (3) the type of final decomposition model,
        # (4) the number N of components in the final model , and
        # (5) the quality flag Q.

        # 6- 8 for unresolved central component ('psf'; phys, frel,  mag),
        # 9-15 for inner sersic-component ('sersic1'; phys,  frel,  mag,    re,    ar,      pa,    n),
        # 16-22 for inner disk-component ('expo1'; phys,  frel,  mag,    hr,    ar,      pa,   mu0),
        # 23-28 for inner ferrers-component ('ferrers1'; phys,  frel,  mu0,   rout,   ar,     pa),
        # 29-34 for inner edge-on disk component ('edgedisk1';  phys, frel, mu0,  rs,    hs,      pa ).
        # 35-41 for outer sersic-component ('sersic2'; phys,  frel,  mag,    re,    ar,      pa,    n),
        # 42-49 for outer disk-component ('expo2'; phys,  frel,  mag,    hr,    ar,      pa,   mu0),
        # 50-55 for outer ferrers-component ('ferrers2'; phys,  frel,  mu0,   rout,   ar,     pa),
        # 56-61 for outer edge-on disk component ('edgedisk2';  phys, frel, mu0,  rs,    hs,      pa ).

        sersic_1_index = 8
        disk_1_index = 15

        edgedisk_1_index = 28

        #For each function:

        #the first entry stands for the physical intepretation of the component:
        #'N' for a central source (or unresolved bulge), 'B' for a bulge (or elliptical), 'D' for a disk, 'BAR' for a bar, and 'Z' for an edge-on disk.

        # 'rel    =  the relative contribution of the component to the total model flux,
        #  mag    =  the component's total 3.6 micron AB magnitude,
        #  mu0    =  the central  surface brightness (mag/arcsec^2;  de-projected central surface brightness for expdisk and edgedisk, and
        #                                                          sky-plane central surface brightness for ferrer)
        #  ar     =  axial ratio
        #  pa     =  position angle (degrees ccw from North)
        #  n      =  sersic index
        #  hr     =  exponential scale lenght (arcsec)
        #  rs     =  radial scale lenght (arcsec)
        #  hs     =  vertical scale height (arcsec)
        #  rout   =  bar outer truncation radius (arcsec)

        # SERSIC: phys,  frel,  mag,    re,    ar,      pa,    n
        # DISK: phys,  frel,  mag,    hr,    ar,      pa,   mu0

        # EDGEDISK: phys frel mu0    rs    hs      pa

        with open(local_table_path, 'r') as s4g_table:

            for line in s4g_table:

                splitted = line.split()

                if len(splitted) < 2: continue

                name = splitted[1]

                #print(list(name))

                # Only look at the line corresponding to the galaxy
                if name != self.ngc_name_nospaces: continue

                #if self.ngc_name_nospaces not in name: continue

                #self.parameters.model_type = splitted[2]
                #self.parameters.number_of_components = splitted[3]
                #self.parameters.quality = splitted[4]

                ## BULGE

                #self.parameters.bulge.interpretation = splitted[sersic_1_index].split("|")[1]
                bulge_f = float(splitted[sersic_1_index + 1])
                mag = float(splitted[sersic_1_index + 2])
                bulge_fluxdensity = unitconversion.ab_to_jansky(mag) * u("Jy")

                # Effective radius in pc
                re_arcsec = float(splitted[sersic_1_index + 3]) * u("arcsec")
                bulge_re = (self.properties.distance * re_arcsec).to(
                    "pc", equivalencies=dimensionless_angles())

                bulge_q = float(splitted[sersic_1_index + 4])
                bulge_pa = Angle(
                    float(splitted[sersic_1_index + 5]) - 90., "deg")
                bulge_n = float(splitted[sersic_1_index + 6])

                # Create the bulge component
                bulge = SersicModel2D(rel_contribution=bulge_f,
                                      fluxdensity=bulge_fluxdensity,
                                      effective_radius=bulge_re,
                                      axial_ratio=bulge_q,
                                      position_angle=bulge_pa,
                                      index=bulge_n)

                # Add the bulge to the components dictionary
                self.components["bulge"] = bulge

                ## DISK

                if splitted[disk_1_index + 1] != "-":

                    #self.parameters.disk.interpretation = splitted[disk_1_index].split("|")[1]
                    disk_f = float(splitted[disk_1_index + 1])
                    mag = float(splitted[disk_1_index + 2])
                    disk_fluxdensity = unitconversion.ab_to_jansky(mag) * u(
                        "Jy")

                    # Scale length in pc
                    hr_arcsec = float(splitted[disk_1_index + 3]) * u("arcsec")
                    disk_hr = (self.properties.distance * hr_arcsec).to(
                        "pc", equivalencies=dimensionless_angles())

                    disk_q = float(splitted[disk_1_index + 4])  # axial ratio
                    disk_pa = Angle(
                        float(splitted[disk_1_index + 5]) - 90., "deg")
                    disk_mu0 = float(
                        splitted[disk_1_index + 6]) * u("mag/arcsec2")

                    # Create the disk component
                    disk = ExponentialDiskModel2D(rel_contribution=disk_f,
                                                  fluxdensity=disk_fluxdensity,
                                                  scalelength=disk_hr,
                                                  axial_ratio=disk_q,
                                                  position_angle=disk_pa,
                                                  mu0=disk_mu0)

                else:

                    # phys frel mu0    rs    hs      pa

                    disk_f = float(splitted[edgedisk_1_index + 1])
                    mag = None
                    fluxdensity = None

                    # frel mu0    rs    hs      pa

                    #print(disk_f)

                    disk_mu0 = float(
                        splitted[edgedisk_1_index + 2]) * u("mag/arcsec2")

                    #  rs    hs      pa

                    #  rs     =  radial scale lenght (arcsec)
                    #  hs     =  vertical scale height (arcsec)

                    rs = float(splitted[edgedisk_1_index + 3])
                    hs = float(splitted[edgedisk_1_index + 4])

                    #print(rs)
                    #print(hs)

                    disk_pa = Angle(
                        float(splitted[edgedisk_1_index + 5]) - 90., "deg")

                    #print(disk_pa)

                    disk_q = hs / rs

                    # Create the disk component
                    disk = ExponentialDiskModel2D(rel_contribution=disk_f,
                                                  scalelength=rs,
                                                  axial_ratio=disk_q,
                                                  position_angle=disk_pa,
                                                  mu0=disk_mu0)

                # Add the disk to the components dictionary
                self.components["disk"] = disk

        # Set the disk position angle
        self.disk_pa = self.components["disk"].position_angle

    # -----------------------------------------------------------------

    def create_bulge_model(self):
        """
        :return:
        """

        # Inform the user
        log.info("Creating the bulge model ...")

        # Create a Sersic model for the bulge
        self.bulge = SersicModel3D.from_2d(self.components["bulge"],
                                           self.properties.inclination,
                                           self.disk_pa)

    # -----------------------------------------------------------------

    def create_disk_model(self):
        """
        :return:
        """

        # Inform the user
        log.info("Creating the disk model ...")

        # Create an exponential disk model for the disk
        self.disk = ExponentialDiskModel3D.from_2d(self.components["disk"],
                                                   self.properties.inclination,
                                                   self.disk_pa)

    # -----------------------------------------------------------------

    def show(self):
        """
        This function ...
        :return:
        """

        print(fmt.green + "Galaxy properties" + fmt.reset + ":")
        print("")
        print(self.properties)
        print("")

        for name in self.components:

            print(fmt.green + name + fmt.reset + ":")
            print("")
            print(self.components[name])
            print("")

    # -----------------------------------------------------------------

    def write(self):
        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Writing ...")

        # Write the properties
        self.write_properties()

        # Write the components
        self.write_components()

    # -----------------------------------------------------------------

    def write_properties(self):
        """
        This function ...
        :return:
        """

        # Determine the path and save
        path = self.output_path_file("properties.dat")
        self.properties.saveto(path)

    # -----------------------------------------------------------------

    def write_components(self):
        """
        This function ...
        :return:
        """

        # Loop over the components
        for name in self.components:

            # Determine the path
            path = self.output_path_file(name + ".mod")

            # Save the model
            self.components[name].saveto(path)
Exemple #30
0
class SEDFetcher(OldConfigurable):

    """
    This class ...
    """
    
    def __init__(self, config=None):
    
        """
        The constructor ...
        """

        # Call the constructor of the base class
        super(SEDFetcher, self).__init__(config, "modeling")

        # -- Attributes --

        # The name of the galaxy
        self.galaxy_name = None

        # The NGC ID of the galaxy
        self.ngc_id = None

        # The Vizier querying object
        self.vizier = Vizier(keywords=["galaxies"])
        self.vizier.ROW_LIMIT = -1

        # The observed SED
        self.seds = dict()

        # The filters
        self.filters = dict()

    # -----------------------------------------------------------------

    @classmethod
    def from_arguments(cls, arguments):

        """
        This function ...
        :param arguments:
        :return:
        """

        # Create a new SEDFetcher instance
        if arguments.config is not None: fetcher = cls(arguments.config)
        elif arguments.settings is not None: fetcher = cls(arguments.settings)
        else: fetcher = cls()

        # Set the output path
        if arguments.output_path is not None: fetcher.config.output_path = arguments.output_path

        # Run from the command line, so always write out the SEDs
        fetcher.config.write_seds = True
        fetcher.config.writing.seds_path = "SEDs"

        # Return the new instance
        return fetcher

    # -----------------------------------------------------------------

    def run(self, galaxy_name):

        """
        This function ...
        :param galaxy_name
        """

        # 1. Call the setup function
        self.setup(galaxy_name)

        # 2. If requested, query the GALEX ultraviolet atlas of nearby galaxies catalog (Gil de Paz+, 2007)
        if "GALEX" in self.config.catalogs: self.get_galex()

        # 3. If requested, query the 2MASS Extended sources catalog (IPAC/UMass, 2003-2006)
        if "2MASS" in self.config.catalogs: self.get_2mass()

        # SDSS ?
        # Ilse "Voor de galaxieen in de noordelijke hemisfeer heb je SDSS data beschikbaar, en die kan je dan
        # beter gebruiken (ipv. SINGS) om je fit te constrainen in het optische gebied. Er zal binnenkort een nieuwe
        # paper van Daniel Dale uitkomen met gehercalibreerde fluxen in de optische banden, die je dan kan gebruiken.

        # 5. If requested, query the Radial distribution in SINGS galaxies (I.) catalogs (Munoz-Mateos+, 2009)
        # Opm. Ilse: "voor vele SINGS galaxieen is de calibratie van de optische data heel slecht, dus krijg je een
        # offset en datapunten die een beetje random blijken te zijn."
        if "SINGS" in self.config.catalogs: self.get_sings()

        # If requested, query the LVL global optical photometry (Cook+, 2014) catalog
        if "LVL" in self.config.catalogs: self.get_lvl()

        # If requested, query the Spitzer Local Volume Legacy: IR photometry (Dale+, 2009)
        if "Spitzer" in self.config.catalogs: self.get_spitzer()

        # If requested, query the Spitzer/IRS ATLAS project source (Hernan-Caballero+, 2011)
        if "Spitzer/IRS" in self.config.catalogs: self.get_spitzer_irs()

        # If requested, query the Compendium of ISO far-IR extragalactic data (Brauher+, 2008)
        if "IRAS" in self.config.catalogs: self.get_iras()

        # If requested, query the Imperial IRAS-FSC redshift catalogue (IIFSCz) (Wang+, 2009)
        if "IRAS-FSC" in self.config.catalogs: self.get_iras_fsc()

        # If requested, query the S4G catalog for IRAC fluxes
        if "S4G" in self.config.catalogs: self.get_s4g()

        # If requested, query the Spectroscopy and abundances of SINGS galaxies (Moustakas+, 2010) catalog
        if "Emission lines" in self.config.catalogs: self.get_emission_lines()

        # If requested, query the Atlas of UV-to-MIR galaxy SEDs (Brown+, 2014)
        if "Brown" in self.config.catalogs: self.get_brown()

        # If requested, query the Planck Catalog of Compact Sources Release 1 (Planck, 2013)
        if "Planck" in self.config.catalogs: self.get_planck()

        # SPECIFIC for M81: not enabled, no time to figure out the unit conversion now
        #if self.ngc_id == "NGC 3031": self.get_m81()

        # Other interesting catalogs:
        # http://vizier.cfa.harvard.edu/viz-bin/VizieR-3?-source=J/ApJS/199/22
        # http://vizier.cfa.harvard.edu/viz-bin/VizieR-3?-source=J/ApJS/212/18/sample&-c=NGC%203031&-c.u=arcmin&-c.r=2&-c.eq=J2000&-c.geom=r&-out.max=50&-out.form=HTML%20Table&-oc.form=sexa
        # http://vizier.cfa.harvard.edu/viz-bin/VizieR-3?-source=J/ApJS/220/6

        # Writing
        self.write()

    # -----------------------------------------------------------------

    def setup(self, galaxy_name):

        """
        This function ...
        :param galaxy_name:
        :return:
        """

        # Call the setup function of the base class
        super(SEDFetcher, self).setup()

        # Set the galaxy name
        self.galaxy_name = galaxy_name

        # Get the NGC ID of the galaxy
        self.ngc_id = catalogs.get_ngc_name(self.galaxy_name)

        # Create a dictionary of filters
        keys = ["Ha", "FUV", "NUV", "U", "B", "V", "R", "J", "H", "K", "IRAS 12", "IRAS 25", "IRAS 60", "IRAS 100", "I1", "I2", "I3", "I4", "MIPS 24", "MIPS 70", "MIPS 160", "SDSS u", "SDSS g", "SDSS r", "SDSS i", "SDSS z"]
        for key in keys: self.filters[key] = Filter.from_string(key)

    # -----------------------------------------------------------------

    def get_galex(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Getting fluxes from the GALEX ultraviolet atlas of nearby galaxies ...")

        # GALEX: "J/ApJS/173/185" of "J/ApJS/173/185/galex": B and V (2007ApJS..173..185G)
        # Interesting columns:
        #
        # - "MajAxis": Major-axis diameter of the D25 ellipse [arcmin]
        # - "MinAxis": Minor-axis diameter of the D25 ellipse [arcmin]
        # - "PA": Position angle of the D25 ellipse [deg]
        # - "Dist": Distance to the galaxy [Mpc]
        # - "Morph": Morphological type
        # - "T": Morphological type T
        # - "FUV": FUV (120-177nm) image mean sky background [ct/s]
        # - "sigFUV": Mean standard deviation of the FUV sky. Note (2): Measured by averaging the standard deviation within several regions around the position of the object. [ct/s]
        # - "e_FUV": The standard deviation of the mean FUV sky [ct/s]
        # - "NUV": Mean NUV (177-300nm) sky background [ct/s]
        # - "sigNUV": Mean standard deviation of the NUV sky [ct/s]
        # - "e_NUV": The standard deviation of the mean NUV sky [ct/s]
        # - "D25FUV": Observed D25 ellipse FUV band AB magnitude [mag]
        # - "e_D25FUV": Uncertainty in D25FUV [mag]
        # - "D25NUV": Observed D25 ellipse NUV band AB magnitude [mag]
        # - "e_D25NUV": Uncertainty in D25NUV [mag]
        # - "AFUV": Foreground FUV extinction [mag]
        # - "ANUV": Foreground NUV extinction [mag]
        # - "asyFUV": Observed asymptotic FUV (120-177nm) AB magnitude [mag]
        # - "e_asyFUV": Uncertainty in asyFUV [mag]
        # - "asyNUV": Observed asymptotic NUV (177-300nm) AB magnitude [mag]
        # - "e_asyNUV": Uncertainty in asyNUV [mag]
        # - "logFUV": Log of the FUV (120-177nm) luminosity [W]
        # - "logNUV": Log of the NUV (177-300nm) luminosity [W]
        # - "Umag": Johnson U band integrated magnitude [mag] Note (1): In the Vega scale. Published as part of the RC3 catalog, Cat. VII/155)
        # - "e_Umag": Uncertainty in Umag [mag]
        # - "Bmag": Johnson B band integrated magnitude [mag]
        # - "e_Bmag": Uncertainty in Bmag [mag]
        # - "Vmag": Johnson V band integrated magnitude [mag]
        # - "e_Vmag": Uncertainty in Vmag [mag]
        # - "Jmag": 2MASS J band total magnitude [mag]
        # - "e_Jmag": Uncertainty in Jmag [mag]
        # - "Hmag": 2MASS H band total magnitude [mag]
        # - "e_Hmag": Uncertainty in Hmag [mag]
        # - "Kmag": 2MASS K band total magnitude [mag]
        # - "e_Kmag": Uncertainty in Kmag [mag]
        # - "F12um": IRAS 12 micron flux density [Jy]
        # - "e_F12um": Uncertainty in F12um [Jy]
        # - "F25um": IRAS 25 micron flux density [Jy]
        # - "e_F25um": Uncertainty in F25um [Jy]
        # - "F60um": IRAS 60 micron flux density [Jy]
        # - "e_F60um": Uncertainty in F60um [Jy]
        # - "F100um": IRAS 100 micron flux density [Jy]
        # - "e_F100um": Uncertainty in F100um [Jy]

        result = self.vizier.query_object(self.galaxy_name, catalog="J/ApJS/173/185/galex")
        # Result is a list of tables, we only have one table with one entry

        # Create an SED
        sed = ObservedSED()

        # All AB magnitudes

        # FUV --

        if not result[0]["asyFUV"].mask[0]:

            fuv_mag = result[0]["asyFUV"][0]
            fuv_mag_error = result[0][0]["e_asyFUV"]
            fuv_mag_lower = fuv_mag - fuv_mag_error
            fuv_mag_upper = fuv_mag + fuv_mag_error

            # flux
            fuv = unitconversion.ab_to_jansky(fuv_mag)
            fuv_lower = unitconversion.ab_to_jansky(fuv_mag_upper)
            fuv_upper = unitconversion.ab_to_jansky(fuv_mag_lower)
            fuv_error = ErrorBar(fuv_lower, fuv_upper, at=fuv)
            sed.add_entry(self.filters["FUV"], fuv, fuv_error)

        # NUV --

        if not result[0]["asyNUV"].mask[0]:

            nuv_mag = result[0][0]["asyNUV"]
            nuv_mag_error = result[0][0]["e_asyNUV"]
            nuv_mag_lower = nuv_mag - nuv_mag_error
            nuv_mag_upper = nuv_mag + nuv_mag_error

            # flux
            nuv = unitconversion.ab_to_jansky(nuv_mag)
            nuv_lower = unitconversion.ab_to_jansky(nuv_mag_upper)
            nuv_upper = unitconversion.ab_to_jansky(nuv_mag_lower)
            nuv_error = ErrorBar(nuv_lower, nuv_upper, at=nuv)
            sed.add_entry(self.filters["NUV"], nuv, nuv_error)

        # U band --

        if not result[0]["Umag"].mask[0]:

            # From Vega magnitude system to AB magnitudes
            u_mag = unitconversion.vega_to_ab(result[0][0]["Umag"], "U")
            u_mag_error = unitconversion.vega_to_ab(result[0][0]["e_Umag"], "U")
            u_mag_lower = u_mag - u_mag_error
            u_mag_upper = u_mag + u_mag_error

            # U band flux
            u = unitconversion.ab_to_jansky(u_mag)
            u_lower = unitconversion.ab_to_jansky(u_mag_upper)
            u_upper = unitconversion.ab_to_jansky(u_mag_lower)
            u_error = ErrorBar(u_lower, u_upper, at=u)
            sed.add_entry(self.filters["U"], u, u_error)

        # B band --

        if not result[0]["Bmag"].mask[0]:

            b_mag = unitconversion.vega_to_ab(result[0][0]["Bmag"], "B")
            b_mag_error = unitconversion.vega_to_ab(result[0][0]["e_Bmag"], "B")
            b_mag_lower = b_mag - abs(b_mag_error)
            b_mag_upper = b_mag + abs(b_mag_error)

            #print("bmag", b_mag)
            #print("bmagerror", b_mag_error)
            #print("bmaglower", b_mag_lower)
            #print("bmagupper", b_mag_upper)

            # B band flux
            b = unitconversion.ab_to_jansky(b_mag)
            b_lower = unitconversion.ab_to_jansky(b_mag_upper)
            b_upper = unitconversion.ab_to_jansky(b_mag_lower)
            b_error = ErrorBar(b_lower, b_upper, at=b)
            sed.add_entry(self.filters["B"], b, b_error)

        # V band --

        if not result[0]["Vmag"].mask[0]:

            v_mag = unitconversion.vega_to_ab(result[0][0]["Vmag"], "V")
            v_mag_error = unitconversion.vega_to_ab(result[0][0]["e_Vmag"], "V")
            v_mag_lower = v_mag - v_mag_error
            v_mag_upper = v_mag + v_mag_error

            # V band flux
            v = unitconversion.ab_to_jansky(v_mag)
            v_lower = unitconversion.ab_to_jansky(v_mag_upper)
            v_upper = unitconversion.ab_to_jansky(v_mag_lower)
            v_error = ErrorBar(v_lower, v_upper, at=v)
            sed.add_entry(self.filters["V"], v, v_error)


        # In 2MASS magnitude system -> can be converted directly into Jy (see below)

        # J band --

        if not result[0]["Jmag"].mask[0]:

            j_mag = result[0][0]["Jmag"]
            j_mag_error = result[0][0]["e_Jmag"]
            j_mag_lower = j_mag - j_mag_error
            j_mag_upper = j_mag + j_mag_error

            # J band flux
            j = unitconversion.photometry_2mass_mag_to_jy(j_mag, "J")
            j_lower = unitconversion.photometry_2mass_mag_to_jy(j_mag_upper, "J")
            j_upper = unitconversion.photometry_2mass_mag_to_jy(j_mag_lower, "J")
            j_error = ErrorBar(j_lower, j_upper, at=j)
            sed.add_entry(self.filters["J"], j, j_error)

        # H band --

        if not result[0]["Hmag"].mask[0]:

            h_mag = result[0][0]["Hmag"]
            h_mag_error = result[0][0]["e_Hmag"]
            h_mag_lower = h_mag - h_mag_error
            h_mag_upper = h_mag + h_mag_error

            # H band flux
            h = unitconversion.photometry_2mass_mag_to_jy(h_mag, "H")
            h_lower = unitconversion.photometry_2mass_mag_to_jy(h_mag_upper, "H")
            h_upper = unitconversion.photometry_2mass_mag_to_jy(h_mag_lower, "H")
            h_error = ErrorBar(h_lower, h_upper, at=h)
            sed.add_entry(self.filters["H"], h, h_error)

        # K band --

        if not result[0]["Kmag"].mask[0]:

            k_mag = result[0][0]["Kmag"]
            k_mag_error = result[0][0]["e_Kmag"]
            k_mag_lower = k_mag - k_mag_error
            k_mag_upper = k_mag + k_mag_error

            # K band flux
            k = unitconversion.photometry_2mass_mag_to_jy(k_mag, "Ks")
            k_lower = unitconversion.photometry_2mass_mag_to_jy(k_mag_upper, "Ks")
            k_upper = unitconversion.photometry_2mass_mag_to_jy(k_mag_lower, "Ks")
            k_error = ErrorBar(k_lower, k_upper, at=k)
            sed.add_entry(self.filters["K"], k, k_error)


        # F12 band flux

        if not result[0]["F12um"].mask[0]:

            f12 = result[0][0]["F12um"]
            f12_error = ErrorBar(result[0][0]["e_F12um"])
            sed.add_entry(self.filters["IRAS 12"], f12, f12_error)

        # F25 band flux

        if not result[0]["F25um"].mask[0]:

            f25 = result[0][0]["F25um"]
            f25_error = ErrorBar(result[0][0]["e_F25um"])
            sed.add_entry(self.filters["IRAS 25"], f25, f25_error)

        # F60 band flux

        if not result[0]["F60um"].mask[0]:

            f60 = result[0][0]["F60um"]
            f60_error = ErrorBar(result[0][0]["e_F60um"])
            sed.add_entry(self.filters["IRAS 60"], f60, f60_error)

        # F100 band flux

        if not result[0]["F100um"].mask[0]:

            f100 = result[0][0]["F100um"]
            f100_error = ErrorBar(result[0][0]["e_F100um"])
            sed.add_entry(self.filters["IRAS 100"], f100, f100_error)

        # Add the SED to the dictionary
        self.seds["GALEX"] = sed

    # -----------------------------------------------------------------

    def get_2mass(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Getting fluxes from the 2MASS Extended Catalog ...")

        # 2MASS Extended Catalog: "VII/233/xsc": J, H, K (2006AJ....131.1163S)
        # Interesting columns:
        #
        # - "J.ext": J magnitude in r.ext (j_m_ext) [mag]
        # - "e_J.ext": σ(J.ext) (j_msig_ext) [mag]
        # - "H.ext": H magnitude in r.ext (h_m_ext) [mag]
        # - "e_H.ext": σ(H.ext) (h_msig_ext) [mag]
        # - "K.ext": J magnitude in r.ext (k_m_ext) [mag]
        # - "e_K.ext": σ(K.ext) (k_msig_ext) [mag]
        result = self.vizier.query_object(self.galaxy_name, catalog="VII/233/xsc")

        # Create an SED
        sed = ObservedSED()

        # In 2MASS magnitude system -> can be converted directly into Jy (see below)
        j_mag = result[0][0]["J.ext"]
        j_mag_error = result[0][0]["e_J.ext"]
        j_mag_lower = j_mag - j_mag_error
        j_mag_upper = j_mag + j_mag_error

        h_mag = result[0][0]["H.ext"]
        h_mag_error = result[0][0]["e_H.ext"]
        h_mag_lower = h_mag - h_mag_error
        h_mag_upper = h_mag + h_mag_error

        k_mag = result[0][0]["K.ext"]
        k_mag_error = result[0][0]["e_K.ext"]
        k_mag_lower = k_mag - k_mag_error
        k_mag_upper = k_mag + k_mag_error

        # J band flux
        j = unitconversion.photometry_2mass_mag_to_jy(j_mag, "J")
        j_lower = unitconversion.photometry_2mass_mag_to_jy(j_mag_upper, "J")
        j_upper = unitconversion.photometry_2mass_mag_to_jy(j_mag_lower, "J")
        j_error = ErrorBar(j_lower, j_upper, at=j)
        sed.add_entry(self.filters["J"], j, j_error)

        # H band flux
        h = unitconversion.photometry_2mass_mag_to_jy(h_mag, "H")
        h_lower = unitconversion.photometry_2mass_mag_to_jy(h_mag_upper, "H")
        h_upper = unitconversion.photometry_2mass_mag_to_jy(h_mag_lower, "H")
        h_error = ErrorBar(h_lower, h_upper, at=h)
        sed.add_entry(self.filters["H"], h, h_error)

        # K band flux
        k = unitconversion.photometry_2mass_mag_to_jy(k_mag, "Ks")
        k_lower = unitconversion.photometry_2mass_mag_to_jy(k_mag_upper, "Ks")
        k_upper = unitconversion.photometry_2mass_mag_to_jy(k_mag_lower, "Ks")
        k_error = ErrorBar(k_lower, k_upper, at=k)
        sed.add_entry(self.filters["K"], k, k_error)

        # Add the SED to the dictionary
        self.seds["2MASS"] = sed

    # -----------------------------------------------------------------

    def get_sings(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Getting fluxes from the SINGS catalog ...")

        # Radial distribution in SINGS galaxies. I.
        # J/ApJ/703/1569/table1	(c)Sample (75 rows)
    	# J/ApJ/703/1569/table2	UV, optical and NIR surface photometry profiles (2206 rows)
    	# J/ApJ/703/1569/table3	UV, optical (SDSS) and NIR photometry profiles (2161 rows)
    	# J/ApJ/703/1569/table4	IRAC and MIPS surface photometry (4319 rows)
    	# J/ApJ/703/1569/table5	UV, optical, and near-IR asymptotic magnitudes for SINGS galaxies lacking SDSS data (43 rows)
    	# J/ApJ/703/1569/table6	UV, optical (SDSS), and near-IR asymptotic magnitudes (32 rows)
    	# J/ApJ/703/1569/table7	IRAC and MIPS asymptotic magnitudes (75 rows)
    	# J/ApJ/703/1569/table8	Non-parametrical morphological estimators (300 rows)

        result = self.vizier.get_catalogs("J/ApJ/703/1569")

        # Result is a TableList with 8 tables (0 to 7)
        # We need:
        # - Table6 -> index 5
        # - Table7 -> index 6

        # Table6
        # - "Name": Galaxy name (NGC ...)
        # - "FUV": GALEX FUV 0.153um-band AB magnitude [mag]
        # - "e_FUV": FUV uncertainty [mag]
        # - "NUV": GALEX NUV 0.227um-band AB magnitude [mag]
        # - "e_NUV": NUV uncertainty [mag]
        # - "umag": SDSS u-band 0.354um AB magnitude [mag]
        # - "e_umag": umag uncertainty [mag]
        # - "gmag": SDSS g-band 0.477um AB magnitude [mag]
        # - "e_gmag": gmag uncertainty [mag]
        # - "rmag": SDSS r-band 0.623um AB magnitude [mag]
        # - "e_rmag": rmag uncertainty [mag]
        # - "imag": SDSS i-band 0.762um AB magnitude [mag]
        # - "e_imag": imag uncertainty [mag]
        # - "zmag": SDSS z-band 0.913um AB magnitude [mag]
        # - "e_zmag": zmag uncertainty [mag]
        # - "Jmag": 2MASS J-band 1.25um AB magnitude [mag]
        # - "e_Jmag": Jmag uncertainty [mag]
        # - "Hmag": 2MASS H-band 1.65um AB magnitude [mag]
        # - "e_Hmag": Hmag uncertainty [mag]
        # - "Ksmag": 2MASS Ks-band 2.17um AB magnitude [mag]
        # - "e_Ksmag": Ksmag uncertainty [mag]

        # Find the row index that corresponds with the specified galaxy

        galaxy_index = tables.find_index(result[5], self.ngc_id)

        # FUV
        fuv_mag = result[5][galaxy_index]["FUV"]
        fuv_mag_error = result[5][galaxy_index]["e_FUV"]
        fuv_mag_lower = fuv_mag - fuv_mag_error
        fuv_mag_upper = fuv_mag + fuv_mag_error

        # NUV
        nuv_mag = result[5][galaxy_index]["NUV"]
        nuv_mag_error = result[5][galaxy_index]["e_NUV"]
        nuv_mag_lower = nuv_mag - nuv_mag_error
        nuv_mag_upper = nuv_mag + nuv_mag_error

        # u
        u_mag = result[5][galaxy_index]["umag"]
        u_mag_error = result[5][galaxy_index]["e_umag"]
        u_mag_lower = u_mag - u_mag_error
        u_mag_upper = u_mag + u_mag_error

        # g
        g_mag = result[5][galaxy_index]["gmag"]
        g_mag_error = result[5][galaxy_index]["e_gmag"]
        g_mag_lower = g_mag - abs(g_mag_error)
        g_mag_upper = g_mag + abs(g_mag_error)

        #print("gmag", g_mag)
        #print("gmagerror", g_mag_error)
        #print("gmaglower", g_mag_lower)
        #print("gmagupper", g_mag_upper)

        # r
        r_mag = result[5][galaxy_index]["rmag"]
        r_mag_error = result[5][galaxy_index]["e_rmag"]
        r_mag_lower = r_mag - r_mag_error
        r_mag_upper = r_mag + r_mag_error

        # i
        i_mag = result[5][galaxy_index]["imag"]
        i_mag_error = result[5][galaxy_index]["e_imag"]
        i_mag_lower = i_mag - i_mag_error
        i_mag_upper = i_mag + i_mag_error

        # z
        z_mag = result[5][galaxy_index]["zmag"]
        z_mag_error = result[5][galaxy_index]["e_zmag"]
        z_mag_lower = z_mag - z_mag_error
        z_mag_upper = z_mag + z_mag_error

        # J
        j_mag = result[5][galaxy_index]["Jmag"]
        j_mag_error = result[5][galaxy_index]["e_Jmag"]
        j_mag_lower = j_mag - j_mag_error
        j_mag_upper = j_mag + j_mag_error

        # H
        h_mag = result[5][galaxy_index]["Hmag"]
        h_mag_error = result[5][galaxy_index]["e_Hmag"]
        h_mag_lower = h_mag - h_mag_error
        h_mag_upper = h_mag + h_mag_error

        # Ks
        k_mag = result[5][galaxy_index]["Ksmag"]
        k_mag_error = result[5][galaxy_index]["e_Ksmag"]
        k_mag_lower = k_mag - k_mag_error
        k_mag_upper = k_mag + k_mag_error


        # Create an SED
        sed = ObservedSED()

        # FUV
        fuv = unitconversion.ab_to_jansky(fuv_mag)
        fuv_lower = unitconversion.ab_to_jansky(fuv_mag_upper)
        fuv_upper = unitconversion.ab_to_jansky(fuv_mag_lower)
        fuv_error = ErrorBar(fuv_lower, fuv_upper, at=fuv)
        sed.add_entry(self.filters["FUV"], fuv, fuv_error)

        # NUV
        nuv = unitconversion.ab_to_jansky(nuv_mag)
        nuv_lower = unitconversion.ab_to_jansky(nuv_mag_upper)
        nuv_upper = unitconversion.ab_to_jansky(nuv_mag_lower)
        nuv_error = ErrorBar(nuv_lower, nuv_upper, at=nuv)
        sed.add_entry(self.filters["NUV"], nuv, nuv_error)

        # u
        u = unitconversion.ab_to_jansky(u_mag)
        u_lower = unitconversion.ab_to_jansky(u_mag_upper)
        u_upper = unitconversion.ab_to_jansky(u_mag_lower)
        u_error = ErrorBar(u_lower, u_upper, at=u)
        sed.add_entry(self.filters["SDSS u"], u, u_error)

        # g
        g = unitconversion.ab_to_jansky(g_mag)
        g_lower = unitconversion.ab_to_jansky(g_mag_upper)
        g_upper = unitconversion.ab_to_jansky(g_mag_lower)
        g_error = ErrorBar(g_lower, g_upper, at=g)
        sed.add_entry(self.filters["SDSS g"], g, g_error)

        # r
        r = unitconversion.ab_to_jansky(r_mag)
        r_lower = unitconversion.ab_to_jansky(r_mag_upper)
        r_upper = unitconversion.ab_to_jansky(r_mag_lower)
        r_error = ErrorBar(r_lower, r_upper, at=r)
        sed.add_entry(self.filters["SDSS r"], r, r_error)

        # i
        i = unitconversion.ab_to_jansky(i_mag)
        i_lower = unitconversion.ab_to_jansky(i_mag_upper)
        i_upper = unitconversion.ab_to_jansky(i_mag_lower)
        i_error = ErrorBar(i_lower, i_upper, at=i)
        sed.add_entry(self.filters["SDSS i"], i, i_error)

        # z
        z = unitconversion.ab_to_jansky(z_mag)
        z_lower = unitconversion.ab_to_jansky(z_mag_upper)
        z_upper = unitconversion.ab_to_jansky(z_mag_lower)
        z_error = ErrorBar(z_lower, z_upper, at=z)
        sed.add_entry(self.filters["SDSS z"], z, z_error)

        # J
        j = unitconversion.ab_to_jansky(j_mag)
        j_lower = unitconversion.ab_to_jansky(j_mag_upper)
        j_upper = unitconversion.ab_to_jansky(j_mag_lower)
        j_error = ErrorBar(j_lower, j_upper, at=j)
        sed.add_entry(self.filters["J"], j, j_error)

        # H
        h = unitconversion.ab_to_jansky(h_mag)
        h_lower = unitconversion.ab_to_jansky(h_mag_upper)
        h_upper = unitconversion.ab_to_jansky(h_mag_lower)
        h_error = ErrorBar(h_lower, h_upper, at=h)
        sed.add_entry(self.filters["H"], h, h_error)

        # Ks
        k = unitconversion.ab_to_jansky(k_mag)
        k_lower = unitconversion.ab_to_jansky(k_mag_upper)
        k_upper = unitconversion.ab_to_jansky(k_mag_lower)
        k_error = ErrorBar(k_lower, k_upper, at=k)
        sed.add_entry(self.filters["K"], k, k_error)

        # Table7: IRAC and MIPS asymptotic magnitudes
        # - "logF3.6": Spitzer/IRAC 3.6um flux density [logJy]
        # - "e_logF3.6": logF3.6 uncertainty [logJy]
        # - "logF4.5": Spitzer/IRAC 4.5um flux density [logJy]
        # - "e_logF4.5": logF4.5 uncertainty [logJy]
        # - "logF5.8": Spitzer/IRAC 5.8um flux density [logJy]
        # - "e_logF5.8": logF5.8 uncertainty [logJy]
        # - "logF8.0": Spiter/IRAC 8.0um flux density [logJy]
        # - "e_logF8.0": logF8.0 uncertainty [logJy]
        # - "logF24": Spiter/MIPS 24um flux density [logJy]
        # - "e_logF24": logF24 uncertainty [logJy]
        # - "logF70": Spiter/MIPS 70um flux density [logJy]
        # - "e_logF70": logF70 uncertainty [logJy]
        # - "logF160": Spiter/MIPS 160um flux density [logJy]
        # - "e_logF160": logF160 uncertainty [logJy]

        # Table7 -> index 6

        galaxy_index = tables.find_index(result[6], self.ngc_id)

        # 3.6 micron
        i1_log = result[6][galaxy_index]["logF3.6"]
        i1_log_error = result[6][galaxy_index]["e_logF3.6"]
        i1_log_lower = i1_log - i1_log_error
        i1_log_upper = i1_log + i1_log_error

        # 4.5 micron
        i2_log = result[6][galaxy_index]["logF4.5"]
        i2_log_error = result[6][galaxy_index]["e_logF4.5"]
        i2_log_lower = i2_log - i2_log_error
        i2_log_upper = i2_log + i2_log_error

        # 5.8 micron
        i3_log = result[6][galaxy_index]["logF5.8"]
        i3_log_error = result[6][galaxy_index]["e_logF5.8"]
        i3_log_lower = i3_log - i3_log_error
        i3_log_upper = i3_log + i3_log_error

        # 8.0 micron
        i4_log = result[6][galaxy_index]["logF8.0"]
        i4_log_error = result[6][galaxy_index]["e_logF8.0"]
        i4_log_lower = i4_log - i4_log_error
        i4_log_upper = i4_log + i4_log_error

        #print("i4log", i4_log)
        #print("i4_log_error", i4_log_error)
        #print("i4_log_lower", i4_log_lower)
        #print("i4_log_upper", i4_log_upper)

        # 24 micron
        mips24_log = result[6][galaxy_index]["logF24"]
        mips24_log_error = result[6][galaxy_index]["e_logF24"]
        mips24_log_lower = mips24_log - mips24_log_error
        mips24_log_upper = mips24_log + mips24_log_error

        # 70 micron
        mips70_log = result[6][galaxy_index]["logF70"]
        mips70_log_error = result[6][galaxy_index]["e_logF70"]
        mips70_log_lower = mips70_log - mips70_log_error
        mips70_log_upper = mips70_log + mips70_log_error

        # 160 micron
        mips160_log = result[6][galaxy_index]["logF160"]
        mips160_log_error = result[6][galaxy_index]["e_logF160"]
        mips160_log_lower = mips160_log - mips160_log_error
        mips160_log_upper = mips160_log + mips160_log_error

        # Calculate data points and errobars in Janskys, add to the SED

        # 3.6 micron
        i1 = 10.**i1_log
        i1_lower = 10.**i1_log_lower
        i1_upper = 10.**i1_log_upper
        i1_error = ErrorBar(i1_lower, i1_upper, at=i1)
        sed.add_entry(self.filters["I1"], i1, i1_error)

        # 4.5 micron
        i2 = 10.**i2_log
        i2_lower = 10.**i2_log_lower
        i2_upper = 10.**i2_log_upper
        i2_error = ErrorBar(i2_lower, i2_upper, at=i2)
        sed.add_entry(self.filters["I2"], i2, i2_error)

        # 5.8 micron
        i3 = 10.**i3_log
        i3_lower = 10.**i3_log_lower
        i3_upper = 10.**i3_log_upper
        i3_error = ErrorBar(i3_lower, i3_upper, at=i3)
        sed.add_entry(self.filters["I3"], i3, i3_error)

        # 8.0 micron
        i4 = 10.**i4_log
        i4_lower = 10.**i4_log_lower
        i4_upper = 10.**i4_log_upper
        i4_error = ErrorBar(i4_lower, i4_upper, at=i4)
        sed.add_entry(self.filters["I4"], i4, i4_error)

        # 24 micron
        mips24 = 10.**mips24_log
        mips24_lower = 10.**mips24_log_lower
        mips24_upper = 10.**mips24_log_upper
        mips24_error = ErrorBar(mips24_lower, mips24_upper, at=mips24)
        sed.add_entry(self.filters["MIPS 24"], mips24, mips24_error)

        # 70 micron
        mips70 = 10.**mips70_log
        mips70_lower = 10.**mips70_log_lower
        mips70_upper = 10.**mips70_log_upper
        mips70_error = ErrorBar(mips70_lower, mips70_upper, at=mips70)
        sed.add_entry(self.filters["MIPS 70"], mips70, mips70_error)

        # 160 micron
        mips160 = 10.**mips160_log
        mips160_lower = 10.**mips160_log_lower
        mips160_upper = 10.**mips160_log_upper
        mips160_error = ErrorBar(mips160_lower, mips160_upper, at=mips160)
        sed.add_entry(self.filters["MIPS 160"], mips160, mips160_error)

        # Add the SED to the dictionary
        self.seds["SINGS"] = sed

    # -----------------------------------------------------------------

    def get_lvl(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Getting fluxes from the LVL catalog ...")

        # Create an SED
        sed = ObservedSED()

        # "J/MNRAS/445/881": LVL global optical photometry (Cook+, 2014)
        #  - "J/MNRAS/445/881/sample": Galaxies of the Spitzer Local Volume Legacy (LVL): properties (table1) and R25 photometry
        #  - "J/MNRAS/445/881/table3": Photometry within the IR apertures of Dale et al. (2009, Cat. J/ApJ/703/517) (258 rows)
        #  - "J/MNRAS/445/881/table4": Photometry within the UV apertures of Lee et al. (2011, Cat. J/ApJS/192/6) (258 rows)

        result = self.vizier.query_object(self.galaxy_name, catalog="J/MNRAS/445/881/sample")

        # ALL IN AB MAGNITUDE SYSTEM
        # Umag
        # Bmag
        # Vmag
        # Rmag
        # umag
        # gmag
        # rmag
        # imag
        # zmag

        # On SimBad, only sdss bands are used, from /sample ...

        relevant_bands = [("U", "U"), ("B", "B"), ("V", "V"), ("R", "R"), ("u", "SDSS u"), ("g", "SDSS g"), ("r", "SDSS r"), ("i", "SDSS i"), ("z", "SDSS z")]
        for band_prefix_catalog, filter_name in relevant_bands:

            column_name = band_prefix_catalog + "mag"
            error_column_name = "e_" + column_name

            # Skip masked values
            if result[0][column_name].mask[0]: continue

            # AB magnitude
            magnitude = result[0][0][column_name]
            magnitude_error = result[0][0][error_column_name]
            magnitude_lower = magnitude - magnitude_error
            magnitude_upper = magnitude + magnitude_error

            # Convert to Jy
            fluxdensity = unitconversion.ab_to_jansky(magnitude)
            fluxdensity_lower = unitconversion.ab_to_jansky(magnitude_upper)
            fluxdensity_upper = unitconversion.ab_to_jansky(magnitude_lower)
            fluxdensity_error = ErrorBar(fluxdensity_lower, fluxdensity_upper, at=fluxdensity)

            # Add data point to SED
            sed.add_entry(self.filters[filter_name], fluxdensity, fluxdensity_error)

        # Add the SED to the dictionary
        self.seds["LVL"] = sed

    # -----------------------------------------------------------------

    def get_spitzer(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Getting fluxes from the Spitzer catalog ...")

        # "J/ApJ/703/517": The Spitzer Local Volume Legacy: IR photometry (Dale+, 2009)
        # "J/ApJ/703/517/sample": Galaxy sample (table 1) and infrared flux densities (table 2) (258 rows)

        # Create an SED
        sed = ObservedSED()

        result = self.vizier.query_object(self.galaxy_name, catalog="J/ApJ/703/517/sample")

        # F1.25: 2MASS J band (1.25 micron) flux density [Jy]
        # e_F1.25: Uncertainty in F1.25 [Jy]
        # F1.65: 2MASS H band (1.65 micron) flux density [Jy]
        # e_F1.65: Uncertainty in F1.65 [Jy]
        # F2.17: 2MASS Ks band (2.17 micron) flux density [Jy]
        # e_F2.17: Uncertainty in F2.17 [Jy]
        # F3.6: Spitzer/IRAC 3.5 micron band flux density [Jy]
        # e_F3.6: Uncertainty in F3.6 [Jy]
        # F4.5: Spitzer/IRAC 4.5 micron band flux density [Jy]
        # e_F4.5: Uncertainty in F4.5 [Jy]
        # F5.8: Spitzer/IRAC 5.8 micron band flux density [Jy]
        # e_F5.8: Uncertainty in F5.8 [Jy]
        # F8.0: Spitzer/IRAC 8.0 micron band flux density [Jy]
        # e_F8.0: Uncertainty in F8.0 [Jy]
        # F24: Spitzer/MIPS 24 micron flux density [Jy]
        # e_F24: Uncertainty in F24 [Jy]
        # F70: Spitzer/MIPS 70 micron band flux density [Jy]
        # e_F70: Uncertainty in F70 [Jy]
        # F160: Spitzer/MIPS 160 micron band flux density [Jy]
        # e_F160: Uncertainty in F160 [Jy]

        relevant_bands = [("1.25", "J"), ("1.65", "H"), ("2.17", "K"), ("3.6", "I1"), ("4.5", "I2"), ("5.8", "I3"), ("8.0", "I4"), ("24", "MIPS 24"), ("70", "MIPS 70"), ("160", "MIPS 160")]
        for band_prefix_catalog, filter_name in relevant_bands:

            column_name = "F" + band_prefix_catalog
            error_column_name = "e_" + column_name

            # Skip masked values
            if result[0][column_name].mask[0]: continue

            # Flux and error already in Jy
            fluxdensity = result[0][0][column_name]
            fluxdensity_error = ErrorBar(result[0][0][error_column_name])

            # Add data point to SED
            sed.add_entry(self.filters[filter_name], fluxdensity, fluxdensity_error)

        # Add the SED to the dictionary
        self.seds["Spitzer"] = sed

    # -----------------------------------------------------------------

    def get_spitzer_irs(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Getting fluxes from the Spitzer/IRS catalog ...")

        # "J/MNRAS/414/500": Spitzer/IRS ATLAS project source (Hernan-Caballero+, 2011)
        # - "J/MNRAS/414/500/catalog": Spitzer/IRS ATLAS project source catalog, version 1.0 (739 rows)

        # !! Parentheses () are converted into underscores _ in the resulting Astropy tables!!

        # F(3.6): IRAC1 (3.6um) flux density [Jy]
        # e_F(3.6): rms uncertainty on F(3.6) [Jy]
        # F(8.0): IRAC4 (8.0um) flux density [Jy]
        # e_F(8.0): rms uncertainty on F(8.0) [Jy]
        # F(24): 24um flux density [Jy]
        # e_F(24): rms uncertainty on F(24) [Jy]
        # F(70): 70um or similar band flux density [Jy]
        # e_F(70): rms uncertainty on F(70) [Jy]

        # Create an SED
        sed = ObservedSED()

        result = self.vizier.query_object(self.galaxy_name, catalog="J/MNRAS/414/500/catalog")

        relevant_bands = [("3.6", "I1"), ("8.0", "I4"), ("24", "MIPS 24"), ("70", "MIPS 70")]
        for band_prefix_catalog, filter_name in relevant_bands:

            column_name = "F_" + band_prefix_catalog + "_"
            error_column_name = "e_" + column_name

            # Skip masked values
            if result[0][column_name].mask[0]: continue

            # Flux and error already in Jy
            fluxdensity = result[0][0][column_name]
            fluxdensity_error = ErrorBar(result[0][0][error_column_name])

            # Add data point to SED
            sed.add_entry(self.filters[filter_name], fluxdensity, fluxdensity_error)

        # Add the SED to the dictionary
        self.seds["Spitzer-IRS"] = sed

    # -----------------------------------------------------------------

    def get_iras(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Getting fluxes from the IRAS catalog ...")

        # "J/ApJS/178/280": Compendium of ISO far-IR extragalactic data (Brauher+, 2008)
        # - "J/ApJS/178/280/table1": *Galaxies and properties

        # F12: IRAS 12um band flux density [Jy]
        # F25: IRAS 25um band flux density [Jy]
        # F60: IRAS 60um band flux density [Jy]
        # F100: IRAS 100um band flux density [Jy]

        # No errors ...

        # Create an SED
        sed = ObservedSED()

        result = self.vizier.query_object(self.galaxy_name, catalog="J/ApJS/178/280/table1")

        relevant_bands = [("12", "IRAS 12"), ("25", "IRAS 25"), ("60", "IRAS 60"), ("100", "IRAS 100")]
        for band_prefix_catalog, filter_name in relevant_bands:

            column_name = "F" + band_prefix_catalog

            # Skip masked values
            if result[0][column_name].mask[0]: continue

            # Flux and error already in Jy
            fluxdensity = result[0][0][column_name]
            fluxdensity_error = ErrorBar(0.0)

            # Add data point to SED
            sed.add_entry(self.filters[filter_name], fluxdensity, fluxdensity_error)

        # Add the SED to the dictionary
        self.seds["IRAS"] = sed

    # -----------------------------------------------------------------

    def get_iras_fsc(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Getting fluxes from the IRAS-FSC catalog ...")

        # "J/MNRAS/398/109": Imperial IRAS-FSC redshift catalogue (IIFSCz) (Wang+, 2009)
        # - "J/MNRAS/398/109/iifsczv4": IIFSCz Catalogue (MRR+LW 18/04/09)[spectrum/SED] (60303 rows)

        # S12um: IRAS-FSC flux at 12um [Jy]
        # S25um: IRAS-FSC flux at 25um [Jy]
        # S60um: IRAS-FSC flux at 60um [Jy]
        # S100um: IRAS-FSC flux at 100um [Jy]

        # not used in Simbad:
        # umag: SDSS u magnitude [mag: which system??]
        # e_umag:
        # gmag:
        # e_gmag:
        # rmag:
        # e_rmag:
        # imag:
        # e_imag:
        # zmag:
        # e_zmag:

        # Jmag: 2MASS J magnitude [mag: which system??]
        # e_Jmag: rms uncertainty on Jmag [mag: which system??]
        # Hmag: 2MASS H magnitude [mag]
        # e_Hmag: rms uncertainty on Hmag [mag]
        # Kmag: 2MASS K magnitude [mag]
        # e_Kmag rms uncertainty on Kmag [mag]

        # Create an SED
        sed = ObservedSED()

        result = self.vizier.query_object(self.galaxy_name, catalog="J/MNRAS/398/109/iifsczv4")

        relevant_bands = [("12", "IRAS 12"), ("25", "IRAS 25"), ("60", "IRAS 60"), ("100", "IRAS 100")]
        for band_prefix_catalog, filter_name in relevant_bands:

            # Flux and error already in Jy
            fluxdensity = result[0][0]["S" + band_prefix_catalog + "um"]
            fluxdensity_error = ErrorBar(0.0)

            # Add data point to SED
            sed.add_entry(self.filters[filter_name], fluxdensity, fluxdensity_error)

        # Add the SED to the dictionary
        self.seds["IRAS-FSC"] = sed

    # -----------------------------------------------------------------

    def get_s4g(self):

        """
        This function ...
        :return:
        """

        # Create an SED
        sed = ObservedSED()

        # Get parameters from S4G catalog
        result = self.vizier.query_object(self.galaxy_name, catalog=["J/PASP/122/1397/s4g"])
        table = result[0]

        # Magnitudes
        i1_mag = table["__3.6_"][0]
        i2_mag = table["__4.5_"][0]
        i1_mag_error = table["e__3.6_"][0]
        i2_mag_error = table["e__4.5_"][0]

        i1_fluxdensity = unitconversion.ab_to_jansky(i1_mag)
        i1_fluxdensity_lower = unitconversion.ab_to_jansky(i1_mag + i1_mag_error)
        i1_fluxdensity_upper = unitconversion.ab_to_jansky(i1_mag - i1_mag_error)
        i1_error = ErrorBar(i1_fluxdensity_lower, i1_fluxdensity_upper, at=i1_fluxdensity)

        # Add data point to SED
        sed.add_entry(self.filters["I1"], i1_fluxdensity, i1_error)

        i2_fluxdensity = unitconversion.ab_to_jansky(i2_mag)
        i2_fluxdensity_lower = unitconversion.ab_to_jansky(i2_mag + i2_mag_error)
        i2_fluxdensity_upper = unitconversion.ab_to_jansky(i2_mag - i2_mag_error)
        i2_error = ErrorBar(i2_fluxdensity_lower, i2_fluxdensity_upper, at=i2_fluxdensity)

        # Add data point to SED
        sed.add_entry(self.filters["I2"], i2_fluxdensity, i2_error)

        # Add the SED to the dictionary
        self.seds["S4G"] = sed

    # -----------------------------------------------------------------

    def get_brown(self):

        """
        This function ...
        :return:
        """

        # J/ApJS/212/18/sample
        # AB magnitudes for the sample with neither foreground nor intrinsic dust extinction corrections, and modeled Milky Way foreground dust extinction

        # Create an SED
        sed = ObservedSED()

        # FUV: [12.5/22.9] GALEX FUV AB band magnitude
        # e_FUV:
        # UVW2:
        # e_UVW2:
        # UVM2:
        # e_UVM2:
        # NUV:
        # e_NUV:
        # UVW1:
        # e_UVW1:
        # Umag: [11.9/15.7] Swift/UVOT U AB band magnitude
        # e_Umag:
        # umag:
        # e_umag:
        # gmag:
        # e_gmag:
        # Vmag:
        # e_Vmag:
        # rmag:
        # e_rmag:
        # imag:
        # e_imag:
        # zmag:
        # e_zmag:
        # Jmag:
        # e_Jmag:
        # Hmag:
        # e_Hmag:
        # Ksmag:
        # e_Ksmag:
        # W1mag:
        # e_W1mag:
        # [3.6]:
        # e_[3.6]:
        # [4.5]:
        # e_[4.5]:
        # W2mag:
        # e_W2mag:
        # [5.8]:
        # e_[5.8]:
        # [8.0]:
        # e_[8.0]:
        # W3mag:
        # e_W3mag:
        # W4mag:
        # e_W4mag:
        # W4'mag: Corrected WISE W4 AB band magnitude
        # e_W4'mag:
        # [24]:
        # e_[24]:

        pass

    # -----------------------------------------------------------------

    def get_planck(self):

        """
        This function ...
        :return:
        """

        # Create an SED
        sed = ObservedSED()

        # The second release is not yet available ... ??

    # -----------------------------------------------------------------

    def get_emission_lines(self):

        """
        This function ...
        :return:
        """

        # Create an SED
        sed = ObservedSED()

        # J/ApJS/190/233/Opt

        # Get result
        result = self.vizier.get_catalogs("J/ApJS/190/233/Opt")
        table = result[0]

        galaxy_index = tables.find_index(table, self.ngc_id, "Name")

        # FHa: The Hα 6563 Angstrom line flux (aW/m2)
        # e_FHa: Uncertainty in Ha (aW/m2)
        # FNII: The [NII] 6584Å line flux (aW/m2)
        # e_FNII: Uncertainty in NII (aW/m2)

        # H alpha
        ha_flux = table["FHa"][galaxy_index] * Unit("aW/m2")
        ha_flux_error = table["e_FHa"][galaxy_index] * Unit("aW/m2")

        # NII
        n2_flux = table["FNII"][galaxy_index] * Unit("aW/m2")
        n2_flux_error = table["e_FNII"][galaxy_index] * Unit("aW/m2")

        ha_filter = self.filters["Ha"]
        ha_wavelength = ha_filter.centerwavelength() * Unit("micron")
        ha_frequency = ha_wavelength.to("Hz", equivalencies=spectral())

        # Calculate flux density
        ha_fluxdensity = (ha_flux / ha_frequency).to("Jy")
        ha_fluxdensity_error = (ha_flux_error / ha_frequency).to("Jy")
        ha_errorbar = ErrorBar(ha_fluxdensity_error)

        # Add entry
        sed.add_entry(ha_filter, ha_fluxdensity, ha_errorbar)

        # Add the SED to the dictionary
        self.seds["Lines"] = sed

    # -----------------------------------------------------------------

    def get_m81(self):

        """
        This function ...
        :return:
        """

        # UV through far-IR analysis of M81 (Perez-Gonzalez+, 2006)
        # "J/ApJ/648/987"
        # Two tables:
        # - "J/ApJ/648/987/table1": Positions and photometry (GLOBALBKG and LOCALBKG cases) for the regions selected at 160um resolution
        # - "J/ApJ/648/987/table2": Positions and photometry for the regions selected at 24um resolution

        # Table 1

        #result = self.vizier.query_object(self.galaxy_name, catalog="J/ApJ/648/987/table1")

        # Looks like this (only 3 matches)
        #1	M81	09 55 32.2	+69 03 59.0	680.0	40.78	43.18	42.84	42.63	42.61	41.98	42.67	42.99
        #2	Reg02	09 55 32.2	+69 03 59.0	64.0	39.67	42.63	42.28	41.98	41.76	41.25	41.83	41.78
        #3	Reg03	09 55 32.2	+69 03 59.0	104.0	39.40	42.38	42.03	41.76	41.58	40.90	41.62	41.78

        #galaxy_index = tables.find_index(result, self.galaxy_name)

        # Table 2

        # Interesting rows:
        # - logLFUV: Log of the FUV luminosity [1e-7 W] or [erg/s]
        # - logLNUV: Log of the NUV luminosity [1e-7 W] or [erg/s]
        # - logLHa: Log of the H-alpha luminosity [1e-7 W] or [erg/s]
        # - logL8: Log of the 8um luminosity [1e-7 W] or [erg/s]
        # - logL24: Log of the 24um luminosity [1e-7 W] or [erg/s]

        result = self.vizier.query_object(self.galaxy_name, catalog="J/ApJ/648/987/table2")

        galaxy_index = tables.find_index(result[0], self.galaxy_name)

        # Create an SED
        sed = ObservedSED()

        relevant_bands = [("FUV", "FUV"), ("NUV", "NUV"), ("Ha", "Ha"), ("8", "I4"), ("24", "MIPS 24")]
        for band_prefix_catalog, filter_name in relevant_bands:

            # Flux and error already in Jy
            log = result[0][galaxy_index]["logL" + band_prefix_catalog]
            flux = 10.**log # In erg/s

            frequency = 0.0 # TODO: calculate this!

            # Also: calculate the amount of flux per unit area ! : divide also by 4 pi d_galaxy**2 !

            fluxdensity = flux / frequency
            fluxdensity_error = ErrorBar(0.0)

            # Add data point to SED
            sed.add_entry(self.filters[filter_name], fluxdensity, fluxdensity_error)

        # Add the SED to the dictionary
        self.seds["IRAS-FSC"] = sed

    # -----------------------------------------------------------------

    def write(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Writing ...")

        # If requested, write out the SEDs
        if self.config.write_seds: self.write_seds()

    # -----------------------------------------------------------------

    def write_seds(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Writing the SEDs ...")

        # Determine the full path to the SEDs directory
        path = self.full_output_path(self.config.writing.seds_path)

        # Create the SEDs directory if necessary
        fs.create_directory(path)

        # Loop over the different SEDs
        for label in self.seds:

            # Debugging info
            log.debug("Writing SED from " + label)

            # Determine the path to the new SED file
            sed_path = fs.join(path, label + ".dat")

            # Save the SED at the specified location
            self.seds[label].save(sed_path)
Exemple #31
0
from astroquery.vizier import Vizier



v = Vizier(columns=['Vmag', 'b-y','m1','c1','Beta'], catalog="II/215/catalog")
result =  v.query_object('HD  48915')[0][0]
print (result)
def find_location_gs(source_name, source_alt_az,
                  minute, hour, day, month, year,
                  plot_grids=True):
    """Find out where we are on Earth.

    Args:
        source_name (str):
        source_ra_dec (tuple of floats):
        minute, hour, day, month, year (ints):

    Returns:
        lat_long (tuple of floats): your location.
    """

    alt, az = source_alt_az
    source_obj = Vizier.query_object(source_name, catalog='V/50')[0]
    source_ra_dec = (source_obj['RAJ2000'][0], source_obj['DEJ2000'][0])

    source_ra_hms = tuple(map(float, source_ra_dec[0].split()))
    source_dec_dms = tuple(map(float, source_ra_dec[1].split()))

    source_ra = Angle(source_ra_hms, unit='hourangle').degree
    source_dec = Angle(source_dec_dms, unit=u.deg).degree

    lats = np.arange(-90., 90, res)
    longs = np.arange(-180, 180, res)

    ra_grid = np.zeros((len(lats), len(longs)))
    dec_grid = np.zeros((len(lats), len(longs)))
    score_grid = np.zeros((len(lats), len(longs)))

    # Run the grid
    lat_counter, long_counter = 0, 0
    for i in range(len(lats)):
        for j in range(len(longs)):
            # Need to sort out angular units
            lat, long = lats[i], longs[j]

            ra, dec = altaz_to_radec((alt, az), pos=(lat, long),
                                     minute=minute, hour=hour, day=day,
                                     month=month, year=year, tz_offset=5)

            # pos_grid[i, j] = {'RA': ra, 'DEC': dec}
            ra_grid[i, j] = ra
            dec_grid[i, j] = dec

            # Bad - planar:
            score = np.sqrt((ra - source_ra)**2 + (dec - source_dec)**2)

            # Good - spherical:
            # score = np.arccos(np.sin(dec) * np.sin(source_dec) + np.cos(dec) * np.cos(source_dec) * np.cos(abs(ra - source_ra)))

            score_grid[i, j] = score

            verbose = False
            if verbose is True:
                print('RA, Source RA:', ra, source_ra)
                print('DEC, Source DEC:', dec, source_dec)
                print('Score:', score)
                print('\n')
            else:
                step = long_counter + lat_counter * len(lats)
                print (str(step) + '/' + str(len(lats) * len(longs)))
            long_counter += 1

    outname = 'latlong-gridsearch-results_' + str(res)
    score_df = pd.DataFrame(score_grid)
    score_df.to_csv(outname + '.csv')

    if plot_grids is True:
        lat_coord = (90 + local_latlong[0]) * res
        long_coord = (180 + local_latlong[1]) * res

        plt.contour(score_grid)
        plt.plot([lat_coord], [long_coord], 'or')
        plt.matshow(score_grid, cmap='magma')

        xtick_locs = np.arange(0, len(longs), len(longs)/6)
        xtick_labs = [int(longs[i]) for i in xtick_locs]
        plt.xticks(xtick_locs, xtick_labs)

        # plt.ylim(max(lats), min(lats))
        ytick_locs = np.arange(0, len(lats), len(lats)/10)
        ytick_labs = [int(lats[i]) for i in ytick_locs]
        plt.yticks(ytick_locs, ytick_labs)

        plt.savefig(outname + '.png', dpi=200)
        plt.show(block=False)


    return {'RA': ra_grid, 'DEC': dec_grid, 'SCORE': score_grid}
def find_location(source_name, source_alt_az,
                  minute=minute_now, hour=hour_now, day=day_now,
                  month=month_now, year=year_now, plot_grids=True):
    """Find out where we are on Earth.

    Args:
        source_name (str):
        source_ra_dec (tuple of floats):
        minute, hour, day, month, year (ints):

    Returns:
        lat_long (tuple of floats): your location.
    """

    alt, az = source_alt_az
    source_obj = Vizier.query_object(source_name, catalog='V/50')[0]
    source_ra_dec = (source_obj['RAJ2000'][0], source_obj['DEJ2000'][0])

    source_ra_hms = tuple(map(float, source_ra_dec[0].split()))
    source_dec_dms = tuple(map(float, source_ra_dec[1].split()))

    source_ra = Angle(source_ra_hms, unit='hourangle').degree
    source_dec = Angle(source_dec_dms, unit=u.deg).degree


    lat_crit = 0.01
    step = 20
    lats = np.linspace(-90, 90, step)
    longs = np.linspace(-180., 180., 2*step)
    lat_min, lat_max = 0, len(lats) - 1
    long_min, long_max = 0, len(longs) - 1

    real_lat, real_long = 41.55, -72.65

    counter = 0
    while abs(lats[1] - lats[0]) > lat_crit:
        lats = np.linspace(lats[lat_min], lats[lat_max], step)
        longs = np.linspace(longs[long_min], longs[long_max], 2*step)

        score_grid = np.zeros((len(lats), len(longs)))

        # Run the grid
        for i in range(len(lats)):
            for j in range(len(longs)):
                lat, long = lats[i], longs[j]

                ra, dec = altaz_to_radec((alt, az), pos=(lat, long),
                                         minute=minute, hour=hour, day=day,
                                         month=month, year=year, tz_offset=5)

                score = np.sqrt((ra - source_ra)**2 + (dec - source_dec)**2)
                score_grid[i, j] = score

        idx = np.where(score_grid == np.nanmin(score_grid))
        lat_min = idx[0][0] - 2 if idx[0][0] > 1 else 0
        lat_max = idx[0][0] + 2 if idx[0][0] < len(lats) - 2 else len(lats) - 1

        long_min = idx[1][0] - 2 if idx[1][0] > 1 else 0
        long_max = idx[1][0] + 2 if idx[1][0] < len(longs) - 2 else len(longs) - 1

        plt.matshow(score_grid, cmap='magma_r')
        plt.contour(score_grid, cmap='magma')
        xtick_locs = np.arange(0, len(longs), len(longs)/6)
        xtick_labs = [int(longs[i]) for i in xtick_locs]
        plt.xticks(xtick_locs, xtick_labs)

        ytick_locs = np.arange(0, len(lats), len(lats)/6)
        ytick_labs = [int(lats[i]) for i in ytick_locs]
        plt.yticks(ytick_locs, ytick_labs)

        i, j = 0, 0
        while longs[i] < real_long and i < len(longs) - 1:
            i += 1
        while lats[j] < real_lat and j < len(lats) - 1:
            j += 1

        print i, j
        if i != 0 and j != 0 and i != len(longs) and i != len(lats):
            plt.plot([i], [j], 'or')
        plt.savefig('window-evolution' + str(counter) + '.png')
        plt.close

        counter += 1

    return (lats[idx[0][0]], longs[idx[1][0]])
















    outname = 'latlong-gridsearch-results_' + str(res)
    score_df = pd.DataFrame(score_grid)
    score_df.to_csv(outname + '.csv')

    if plot_grids is True:
        lat_coord = (90 + local_latlong[0]) * res
        long_coord = (180 + local_latlong[1]) * res

        plt.contour(score_grid)
        plt.plot([lat_coord], [long_coord], 'or')
        plt.matshow(score_grid, cmap='magma')

        xtick_locs = np.arange(0, len(longs), len(longs)/6)
        xtick_labs = [int(longs[i]) for i in xtick_locs]
        plt.xticks(xtick_locs, xtick_labs)

        # plt.ylim(max(lats), min(lats))
        ytick_locs = np.arange(0, len(lats), len(lats)/10)
        ytick_labs = [int(lats[i]) for i in ytick_locs]
        plt.yticks(ytick_locs, ytick_labs)

        plt.savefig(outname + '.png', dpi=200)
        plt.show(block=False)


    return {'RA': ra_grid, 'DEC': dec_grid, 'SCORE': score_grid}
from astroquery.simbad import Simbad

S = Simbad()
S.add_votable_fields('rv_value')

dir = sys.argv[1]
files = glob.glob(dir + '/*p08.fits')
templates = glob.glob('/home/rajikak/tools/RV_standards/*')

print("*************")

for file in files:
    a = pyfits.open(file)
    object_name = a[1].header['OBJNAME']

    #get RV data on the standard:
    #From Vizier table:
    result = Vizier.query_object(object_name)
    interesting_table = result['J/ApJS/141/503/table1']
    object_RV = float(interesting_table[0]['__RV_']) / 1000.

    flux_stamp, wave = ps.read_and_find_star_p08(file)
    spectrum, sig = ps.weighted_extract_spectrum(flux_stamp)
    rv, rv_sig = ps.calc_rv_template(spectrum, wave, sig, templates,
                                     ([0, 5400], [6870, 6890]))
    rv += a[1].header['RADVEL']

    print("object_name: " + object_name)
    print("object RV: " + str(object_RV) + "km/s")
    print("Retrieved RV from standards: " + str(rv) + "km/s")
    print("*************")
def vizier_query(obj, params=True, method='both', coordinate=False):
    """Give mean/median values of some parameters for an object.
    This script use VizieR for looking up the object.

    :obj: The object to query (e.g. HD20010).
    :parama: Extra parameters to look for (default is Teff, logg, __Fe_H_).
    :method: Print median, main or both

    :returns: A dictionary with the parameters
    """

    with warnings.catch_warnings():
        warnings.simplefilter('ignore')
        cat = Vizier.query_object(obj)

    if coordinate:
        for c in cat:
            try:
                ra = c['RAJ2000'][0]
                dec = c['DEJ2000'][0]
            except KeyError:
                ra = 0
            if ra != 0:
                break
        print('\n\n%s %s %s' % (obj, ra, dec))
    else:
        print('\n\nObject: %s' % obj)

    parameters = {'Teff': [], 'logg': [], '__Fe_H_': []}
    if params:
        params=['Teff', 'logg', '__Fe_H_']
        for param in params:
            parameters[param] = []

        for ci in cat:
            for column in parameters.keys():
                try:
                    parameters[column].append(ci[column].quantity)
                except (TypeError, KeyError):
                    pass

        for key in parameters.keys():
            pi = parameters[key]
            parameters[key] = _q2a(pi)
            if len(parameters[key]):
                mean = round(np.nanmean(parameters[key]), 2)
                median = round(np.nanmedian(parameters[key]), 2)
            else:
                mean = 'Not available'
                median = 'Not available'

            if key.startswith('__'):
                key = '[Fe/H]'
            if method == 'mean':
                print('\n%s:\tMean value: %s' % (key, mean))
            elif method == 'median':
                print('\n%s\tMedian value: %s' % (key, median))
            else:
                print('\n%s:\tMean value: %s' % (key, mean))
                print('%s:\tMedian value: %s' % (key, median))

    return parameters, cat
Exemple #36
0
class SEDFetcher(OldConfigurable):
    """
    This class ...
    """
    def __init__(self, config=None):
        """
        The constructor ...
        """

        # Call the constructor of the base class
        super(SEDFetcher, self).__init__(config, "modeling")

        # -- Attributes --

        # The name of the galaxy
        self.galaxy_name = None

        # The NGC ID of the galaxy
        self.ngc_id = None

        # The Vizier querying object
        self.vizier = Vizier(keywords=["galaxies"])
        self.vizier.ROW_LIMIT = -1

        # The observed SED
        self.seds = dict()

        # The filters
        self.filters = dict()

    # -----------------------------------------------------------------

    @classmethod
    def from_arguments(cls, arguments):
        """
        This function ...
        :param arguments:
        :return:
        """

        # Create a new SEDFetcher instance
        if arguments.config is not None: fetcher = cls(arguments.config)
        elif arguments.settings is not None: fetcher = cls(arguments.settings)
        else: fetcher = cls()

        # Set the output path
        if arguments.output_path is not None:
            fetcher.config.output_path = arguments.output_path

        # Run from the command line, so always write out the SEDs
        fetcher.config.write_seds = True
        fetcher.config.writing.seds_path = "SEDs"

        # Return the new instance
        return fetcher

    # -----------------------------------------------------------------

    def run(self, galaxy_name):
        """
        This function ...
        :param galaxy_name
        """

        # 1. Call the setup function
        self.setup(galaxy_name)

        # 2. If requested, query the GALEX ultraviolet atlas of nearby galaxies catalog (Gil de Paz+, 2007)
        if "GALEX" in self.config.catalogs: self.get_galex()

        # 3. If requested, query the 2MASS Extended sources catalog (IPAC/UMass, 2003-2006)
        if "2MASS" in self.config.catalogs: self.get_2mass()

        # SDSS ?
        # Ilse "Voor de galaxieen in de noordelijke hemisfeer heb je SDSS data beschikbaar, en die kan je dan
        # beter gebruiken (ipv. SINGS) om je fit te constrainen in het optische gebied. Er zal binnenkort een nieuwe
        # paper van Daniel Dale uitkomen met gehercalibreerde fluxen in de optische banden, die je dan kan gebruiken.

        # 5. If requested, query the Radial distribution in SINGS galaxies (I.) catalogs (Munoz-Mateos+, 2009)
        # Opm. Ilse: "voor vele SINGS galaxieen is de calibratie van de optische data heel slecht, dus krijg je een
        # offset en datapunten die een beetje random blijken te zijn."
        if "SINGS" in self.config.catalogs: self.get_sings()

        # If requested, query the LVL global optical photometry (Cook+, 2014) catalog
        if "LVL" in self.config.catalogs: self.get_lvl()

        # If requested, query the Spitzer Local Volume Legacy: IR photometry (Dale+, 2009)
        if "Spitzer" in self.config.catalogs: self.get_spitzer()

        # If requested, query the Spitzer/IRS ATLAS project source (Hernan-Caballero+, 2011)
        if "Spitzer/IRS" in self.config.catalogs: self.get_spitzer_irs()

        # If requested, query the Compendium of ISO far-IR extragalactic data (Brauher+, 2008)
        if "IRAS" in self.config.catalogs: self.get_iras()

        # If requested, query the Imperial IRAS-FSC redshift catalogue (IIFSCz) (Wang+, 2009)
        if "IRAS-FSC" in self.config.catalogs: self.get_iras_fsc()

        # If requested, query the S4G catalog for IRAC fluxes
        if "S4G" in self.config.catalogs: self.get_s4g()

        # If requested, query the Spectroscopy and abundances of SINGS galaxies (Moustakas+, 2010) catalog
        if "Emission lines" in self.config.catalogs: self.get_emission_lines()

        # If requested, query the Atlas of UV-to-MIR galaxy SEDs (Brown+, 2014)
        if "Brown" in self.config.catalogs: self.get_brown()

        # If requested, query the Planck Catalog of Compact Sources Release 1 (Planck, 2013)
        if "Planck" in self.config.catalogs: self.get_planck()

        # SPECIFIC for M81: not enabled, no time to figure out the unit conversion now
        #if self.ngc_id == "NGC 3031": self.get_m81()

        # Other interesting catalogs:
        # http://vizier.cfa.harvard.edu/viz-bin/VizieR-3?-source=J/ApJS/199/22
        # http://vizier.cfa.harvard.edu/viz-bin/VizieR-3?-source=J/ApJS/212/18/sample&-c=NGC%203031&-c.u=arcmin&-c.r=2&-c.eq=J2000&-c.geom=r&-out.max=50&-out.form=HTML%20Table&-oc.form=sexa
        # http://vizier.cfa.harvard.edu/viz-bin/VizieR-3?-source=J/ApJS/220/6

        # Writing
        self.write()

    # -----------------------------------------------------------------

    def setup(self, galaxy_name):
        """
        This function ...
        :param galaxy_name:
        :return:
        """

        # Call the setup function of the base class
        super(SEDFetcher, self).setup()

        # Set the galaxy name
        self.galaxy_name = galaxy_name

        # Get the NGC ID of the galaxy
        self.ngc_id = catalogs.get_ngc_name(self.galaxy_name)

        # Create a dictionary of filters
        keys = [
            "Ha", "FUV", "NUV", "U", "B", "V", "R", "J", "H", "K", "IRAS 12",
            "IRAS 25", "IRAS 60", "IRAS 100", "I1", "I2", "I3", "I4",
            "MIPS 24", "MIPS 70", "MIPS 160", "SDSS u", "SDSS g", "SDSS r",
            "SDSS i", "SDSS z"
        ]
        for key in keys:
            self.filters[key] = Filter.from_string(key)

    # -----------------------------------------------------------------

    def get_galex(self):
        """
        This function ...
        :return:
        """

        # Inform the user
        log.info(
            "Getting fluxes from the GALEX ultraviolet atlas of nearby galaxies ..."
        )

        # GALEX: "J/ApJS/173/185" of "J/ApJS/173/185/galex": B and V (2007ApJS..173..185G)
        # Interesting columns:
        #
        # - "MajAxis": Major-axis diameter of the D25 ellipse [arcmin]
        # - "MinAxis": Minor-axis diameter of the D25 ellipse [arcmin]
        # - "PA": Position angle of the D25 ellipse [deg]
        # - "Dist": Distance to the galaxy [Mpc]
        # - "Morph": Morphological type
        # - "T": Morphological type T
        # - "FUV": FUV (120-177nm) image mean sky background [ct/s]
        # - "sigFUV": Mean standard deviation of the FUV sky. Note (2): Measured by averaging the standard deviation within several regions around the position of the object. [ct/s]
        # - "e_FUV": The standard deviation of the mean FUV sky [ct/s]
        # - "NUV": Mean NUV (177-300nm) sky background [ct/s]
        # - "sigNUV": Mean standard deviation of the NUV sky [ct/s]
        # - "e_NUV": The standard deviation of the mean NUV sky [ct/s]
        # - "D25FUV": Observed D25 ellipse FUV band AB magnitude [mag]
        # - "e_D25FUV": Uncertainty in D25FUV [mag]
        # - "D25NUV": Observed D25 ellipse NUV band AB magnitude [mag]
        # - "e_D25NUV": Uncertainty in D25NUV [mag]
        # - "AFUV": Foreground FUV extinction [mag]
        # - "ANUV": Foreground NUV extinction [mag]
        # - "asyFUV": Observed asymptotic FUV (120-177nm) AB magnitude [mag]
        # - "e_asyFUV": Uncertainty in asyFUV [mag]
        # - "asyNUV": Observed asymptotic NUV (177-300nm) AB magnitude [mag]
        # - "e_asyNUV": Uncertainty in asyNUV [mag]
        # - "logFUV": Log of the FUV (120-177nm) luminosity [W]
        # - "logNUV": Log of the NUV (177-300nm) luminosity [W]
        # - "Umag": Johnson U band integrated magnitude [mag] Note (1): In the Vega scale. Published as part of the RC3 catalog, Cat. VII/155)
        # - "e_Umag": Uncertainty in Umag [mag]
        # - "Bmag": Johnson B band integrated magnitude [mag]
        # - "e_Bmag": Uncertainty in Bmag [mag]
        # - "Vmag": Johnson V band integrated magnitude [mag]
        # - "e_Vmag": Uncertainty in Vmag [mag]
        # - "Jmag": 2MASS J band total magnitude [mag]
        # - "e_Jmag": Uncertainty in Jmag [mag]
        # - "Hmag": 2MASS H band total magnitude [mag]
        # - "e_Hmag": Uncertainty in Hmag [mag]
        # - "Kmag": 2MASS K band total magnitude [mag]
        # - "e_Kmag": Uncertainty in Kmag [mag]
        # - "F12um": IRAS 12 micron flux density [Jy]
        # - "e_F12um": Uncertainty in F12um [Jy]
        # - "F25um": IRAS 25 micron flux density [Jy]
        # - "e_F25um": Uncertainty in F25um [Jy]
        # - "F60um": IRAS 60 micron flux density [Jy]
        # - "e_F60um": Uncertainty in F60um [Jy]
        # - "F100um": IRAS 100 micron flux density [Jy]
        # - "e_F100um": Uncertainty in F100um [Jy]

        result = self.vizier.query_object(self.galaxy_name,
                                          catalog="J/ApJS/173/185/galex")
        # Result is a list of tables, we only have one table with one entry

        # Create an SED
        sed = ObservedSED()

        # All AB magnitudes

        # FUV --

        if not result[0]["asyFUV"].mask[0]:

            fuv_mag = result[0]["asyFUV"][0]
            fuv_mag_error = result[0][0]["e_asyFUV"]
            fuv_mag_lower = fuv_mag - fuv_mag_error
            fuv_mag_upper = fuv_mag + fuv_mag_error

            # flux
            fuv = unitconversion.ab_to_jansky(fuv_mag)
            fuv_lower = unitconversion.ab_to_jansky(fuv_mag_upper)
            fuv_upper = unitconversion.ab_to_jansky(fuv_mag_lower)
            fuv_error = ErrorBar(fuv_lower, fuv_upper, at=fuv)
            sed.add_entry(self.filters["FUV"], fuv, fuv_error)

        # NUV --

        if not result[0]["asyNUV"].mask[0]:

            nuv_mag = result[0][0]["asyNUV"]
            nuv_mag_error = result[0][0]["e_asyNUV"]
            nuv_mag_lower = nuv_mag - nuv_mag_error
            nuv_mag_upper = nuv_mag + nuv_mag_error

            # flux
            nuv = unitconversion.ab_to_jansky(nuv_mag)
            nuv_lower = unitconversion.ab_to_jansky(nuv_mag_upper)
            nuv_upper = unitconversion.ab_to_jansky(nuv_mag_lower)
            nuv_error = ErrorBar(nuv_lower, nuv_upper, at=nuv)
            sed.add_entry(self.filters["NUV"], nuv, nuv_error)

        # U band --

        if not result[0]["Umag"].mask[0]:

            # From Vega magnitude system to AB magnitudes
            u_mag = unitconversion.vega_to_ab(result[0][0]["Umag"], "U")
            u_mag_error = unitconversion.vega_to_ab(result[0][0]["e_Umag"],
                                                    "U")
            u_mag_lower = u_mag - u_mag_error
            u_mag_upper = u_mag + u_mag_error

            # U band flux
            u = unitconversion.ab_to_jansky(u_mag)
            u_lower = unitconversion.ab_to_jansky(u_mag_upper)
            u_upper = unitconversion.ab_to_jansky(u_mag_lower)
            u_error = ErrorBar(u_lower, u_upper, at=u)
            sed.add_entry(self.filters["U"], u, u_error)

        # B band --

        if not result[0]["Bmag"].mask[0]:

            b_mag = unitconversion.vega_to_ab(result[0][0]["Bmag"], "B")
            b_mag_error = unitconversion.vega_to_ab(result[0][0]["e_Bmag"],
                                                    "B")
            b_mag_lower = b_mag - abs(b_mag_error)
            b_mag_upper = b_mag + abs(b_mag_error)

            #print("bmag", b_mag)
            #print("bmagerror", b_mag_error)
            #print("bmaglower", b_mag_lower)
            #print("bmagupper", b_mag_upper)

            # B band flux
            b = unitconversion.ab_to_jansky(b_mag)
            b_lower = unitconversion.ab_to_jansky(b_mag_upper)
            b_upper = unitconversion.ab_to_jansky(b_mag_lower)
            b_error = ErrorBar(b_lower, b_upper, at=b)
            sed.add_entry(self.filters["B"], b, b_error)

        # V band --

        if not result[0]["Vmag"].mask[0]:

            v_mag = unitconversion.vega_to_ab(result[0][0]["Vmag"], "V")
            v_mag_error = unitconversion.vega_to_ab(result[0][0]["e_Vmag"],
                                                    "V")
            v_mag_lower = v_mag - v_mag_error
            v_mag_upper = v_mag + v_mag_error

            # V band flux
            v = unitconversion.ab_to_jansky(v_mag)
            v_lower = unitconversion.ab_to_jansky(v_mag_upper)
            v_upper = unitconversion.ab_to_jansky(v_mag_lower)
            v_error = ErrorBar(v_lower, v_upper, at=v)
            sed.add_entry(self.filters["V"], v, v_error)

        # In 2MASS magnitude system -> can be converted directly into Jy (see below)

        # J band --

        if not result[0]["Jmag"].mask[0]:

            j_mag = result[0][0]["Jmag"]
            j_mag_error = result[0][0]["e_Jmag"]
            j_mag_lower = j_mag - j_mag_error
            j_mag_upper = j_mag + j_mag_error

            # J band flux
            j = unitconversion.photometry_2mass_mag_to_jy(j_mag, "J")
            j_lower = unitconversion.photometry_2mass_mag_to_jy(
                j_mag_upper, "J")
            j_upper = unitconversion.photometry_2mass_mag_to_jy(
                j_mag_lower, "J")
            j_error = ErrorBar(j_lower, j_upper, at=j)
            sed.add_entry(self.filters["J"], j, j_error)

        # H band --

        if not result[0]["Hmag"].mask[0]:

            h_mag = result[0][0]["Hmag"]
            h_mag_error = result[0][0]["e_Hmag"]
            h_mag_lower = h_mag - h_mag_error
            h_mag_upper = h_mag + h_mag_error

            # H band flux
            h = unitconversion.photometry_2mass_mag_to_jy(h_mag, "H")
            h_lower = unitconversion.photometry_2mass_mag_to_jy(
                h_mag_upper, "H")
            h_upper = unitconversion.photometry_2mass_mag_to_jy(
                h_mag_lower, "H")
            h_error = ErrorBar(h_lower, h_upper, at=h)
            sed.add_entry(self.filters["H"], h, h_error)

        # K band --

        if not result[0]["Kmag"].mask[0]:

            k_mag = result[0][0]["Kmag"]
            k_mag_error = result[0][0]["e_Kmag"]
            k_mag_lower = k_mag - k_mag_error
            k_mag_upper = k_mag + k_mag_error

            # K band flux
            k = unitconversion.photometry_2mass_mag_to_jy(k_mag, "Ks")
            k_lower = unitconversion.photometry_2mass_mag_to_jy(
                k_mag_upper, "Ks")
            k_upper = unitconversion.photometry_2mass_mag_to_jy(
                k_mag_lower, "Ks")
            k_error = ErrorBar(k_lower, k_upper, at=k)
            sed.add_entry(self.filters["K"], k, k_error)

        # F12 band flux

        if not result[0]["F12um"].mask[0]:

            f12 = result[0][0]["F12um"]
            f12_error = ErrorBar(result[0][0]["e_F12um"])
            sed.add_entry(self.filters["IRAS 12"], f12, f12_error)

        # F25 band flux

        if not result[0]["F25um"].mask[0]:

            f25 = result[0][0]["F25um"]
            f25_error = ErrorBar(result[0][0]["e_F25um"])
            sed.add_entry(self.filters["IRAS 25"], f25, f25_error)

        # F60 band flux

        if not result[0]["F60um"].mask[0]:

            f60 = result[0][0]["F60um"]
            f60_error = ErrorBar(result[0][0]["e_F60um"])
            sed.add_entry(self.filters["IRAS 60"], f60, f60_error)

        # F100 band flux

        if not result[0]["F100um"].mask[0]:

            f100 = result[0][0]["F100um"]
            f100_error = ErrorBar(result[0][0]["e_F100um"])
            sed.add_entry(self.filters["IRAS 100"], f100, f100_error)

        # Add the SED to the dictionary
        self.seds["GALEX"] = sed

    # -----------------------------------------------------------------

    def get_2mass(self):
        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Getting fluxes from the 2MASS Extended Catalog ...")

        # 2MASS Extended Catalog: "VII/233/xsc": J, H, K (2006AJ....131.1163S)
        # Interesting columns:
        #
        # - "J.ext": J magnitude in r.ext (j_m_ext) [mag]
        # - "e_J.ext": σ(J.ext) (j_msig_ext) [mag]
        # - "H.ext": H magnitude in r.ext (h_m_ext) [mag]
        # - "e_H.ext": σ(H.ext) (h_msig_ext) [mag]
        # - "K.ext": J magnitude in r.ext (k_m_ext) [mag]
        # - "e_K.ext": σ(K.ext) (k_msig_ext) [mag]
        result = self.vizier.query_object(self.galaxy_name,
                                          catalog="VII/233/xsc")

        # Create an SED
        sed = ObservedSED()

        # In 2MASS magnitude system -> can be converted directly into Jy (see below)
        j_mag = result[0][0]["J.ext"]
        j_mag_error = result[0][0]["e_J.ext"]
        j_mag_lower = j_mag - j_mag_error
        j_mag_upper = j_mag + j_mag_error

        h_mag = result[0][0]["H.ext"]
        h_mag_error = result[0][0]["e_H.ext"]
        h_mag_lower = h_mag - h_mag_error
        h_mag_upper = h_mag + h_mag_error

        k_mag = result[0][0]["K.ext"]
        k_mag_error = result[0][0]["e_K.ext"]
        k_mag_lower = k_mag - k_mag_error
        k_mag_upper = k_mag + k_mag_error

        # J band flux
        j = unitconversion.photometry_2mass_mag_to_jy(j_mag, "J")
        j_lower = unitconversion.photometry_2mass_mag_to_jy(j_mag_upper, "J")
        j_upper = unitconversion.photometry_2mass_mag_to_jy(j_mag_lower, "J")
        j_error = ErrorBar(j_lower, j_upper, at=j)
        sed.add_entry(self.filters["J"], j, j_error)

        # H band flux
        h = unitconversion.photometry_2mass_mag_to_jy(h_mag, "H")
        h_lower = unitconversion.photometry_2mass_mag_to_jy(h_mag_upper, "H")
        h_upper = unitconversion.photometry_2mass_mag_to_jy(h_mag_lower, "H")
        h_error = ErrorBar(h_lower, h_upper, at=h)
        sed.add_entry(self.filters["H"], h, h_error)

        # K band flux
        k = unitconversion.photometry_2mass_mag_to_jy(k_mag, "Ks")
        k_lower = unitconversion.photometry_2mass_mag_to_jy(k_mag_upper, "Ks")
        k_upper = unitconversion.photometry_2mass_mag_to_jy(k_mag_lower, "Ks")
        k_error = ErrorBar(k_lower, k_upper, at=k)
        sed.add_entry(self.filters["K"], k, k_error)

        # Add the SED to the dictionary
        self.seds["2MASS"] = sed

    # -----------------------------------------------------------------

    def get_sings(self):
        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Getting fluxes from the SINGS catalog ...")

        # Radial distribution in SINGS galaxies. I.
        # J/ApJ/703/1569/table1	(c)Sample (75 rows)
        # J/ApJ/703/1569/table2	UV, optical and NIR surface photometry profiles (2206 rows)
        # J/ApJ/703/1569/table3	UV, optical (SDSS) and NIR photometry profiles (2161 rows)
        # J/ApJ/703/1569/table4	IRAC and MIPS surface photometry (4319 rows)
        # J/ApJ/703/1569/table5	UV, optical, and near-IR asymptotic magnitudes for SINGS galaxies lacking SDSS data (43 rows)
        # J/ApJ/703/1569/table6	UV, optical (SDSS), and near-IR asymptotic magnitudes (32 rows)
        # J/ApJ/703/1569/table7	IRAC and MIPS asymptotic magnitudes (75 rows)
        # J/ApJ/703/1569/table8	Non-parametrical morphological estimators (300 rows)

        result = self.vizier.get_catalogs("J/ApJ/703/1569")

        # Result is a TableList with 8 tables (0 to 7)
        # We need:
        # - Table6 -> index 5
        # - Table7 -> index 6

        # Table6
        # - "Name": Galaxy name (NGC ...)
        # - "FUV": GALEX FUV 0.153um-band AB magnitude [mag]
        # - "e_FUV": FUV uncertainty [mag]
        # - "NUV": GALEX NUV 0.227um-band AB magnitude [mag]
        # - "e_NUV": NUV uncertainty [mag]
        # - "umag": SDSS u-band 0.354um AB magnitude [mag]
        # - "e_umag": umag uncertainty [mag]
        # - "gmag": SDSS g-band 0.477um AB magnitude [mag]
        # - "e_gmag": gmag uncertainty [mag]
        # - "rmag": SDSS r-band 0.623um AB magnitude [mag]
        # - "e_rmag": rmag uncertainty [mag]
        # - "imag": SDSS i-band 0.762um AB magnitude [mag]
        # - "e_imag": imag uncertainty [mag]
        # - "zmag": SDSS z-band 0.913um AB magnitude [mag]
        # - "e_zmag": zmag uncertainty [mag]
        # - "Jmag": 2MASS J-band 1.25um AB magnitude [mag]
        # - "e_Jmag": Jmag uncertainty [mag]
        # - "Hmag": 2MASS H-band 1.65um AB magnitude [mag]
        # - "e_Hmag": Hmag uncertainty [mag]
        # - "Ksmag": 2MASS Ks-band 2.17um AB magnitude [mag]
        # - "e_Ksmag": Ksmag uncertainty [mag]

        # Find the row index that corresponds with the specified galaxy

        galaxy_index = tables.find_index(result[5], self.ngc_id)

        # FUV
        fuv_mag = result[5][galaxy_index]["FUV"]
        fuv_mag_error = result[5][galaxy_index]["e_FUV"]
        fuv_mag_lower = fuv_mag - fuv_mag_error
        fuv_mag_upper = fuv_mag + fuv_mag_error

        # NUV
        nuv_mag = result[5][galaxy_index]["NUV"]
        nuv_mag_error = result[5][galaxy_index]["e_NUV"]
        nuv_mag_lower = nuv_mag - nuv_mag_error
        nuv_mag_upper = nuv_mag + nuv_mag_error

        # u
        u_mag = result[5][galaxy_index]["umag"]
        u_mag_error = result[5][galaxy_index]["e_umag"]
        u_mag_lower = u_mag - u_mag_error
        u_mag_upper = u_mag + u_mag_error

        # g
        g_mag = result[5][galaxy_index]["gmag"]
        g_mag_error = result[5][galaxy_index]["e_gmag"]
        g_mag_lower = g_mag - abs(g_mag_error)
        g_mag_upper = g_mag + abs(g_mag_error)

        #print("gmag", g_mag)
        #print("gmagerror", g_mag_error)
        #print("gmaglower", g_mag_lower)
        #print("gmagupper", g_mag_upper)

        # r
        r_mag = result[5][galaxy_index]["rmag"]
        r_mag_error = result[5][galaxy_index]["e_rmag"]
        r_mag_lower = r_mag - r_mag_error
        r_mag_upper = r_mag + r_mag_error

        # i
        i_mag = result[5][galaxy_index]["imag"]
        i_mag_error = result[5][galaxy_index]["e_imag"]
        i_mag_lower = i_mag - i_mag_error
        i_mag_upper = i_mag + i_mag_error

        # z
        z_mag = result[5][galaxy_index]["zmag"]
        z_mag_error = result[5][galaxy_index]["e_zmag"]
        z_mag_lower = z_mag - z_mag_error
        z_mag_upper = z_mag + z_mag_error

        # J
        j_mag = result[5][galaxy_index]["Jmag"]
        j_mag_error = result[5][galaxy_index]["e_Jmag"]
        j_mag_lower = j_mag - j_mag_error
        j_mag_upper = j_mag + j_mag_error

        # H
        h_mag = result[5][galaxy_index]["Hmag"]
        h_mag_error = result[5][galaxy_index]["e_Hmag"]
        h_mag_lower = h_mag - h_mag_error
        h_mag_upper = h_mag + h_mag_error

        # Ks
        k_mag = result[5][galaxy_index]["Ksmag"]
        k_mag_error = result[5][galaxy_index]["e_Ksmag"]
        k_mag_lower = k_mag - k_mag_error
        k_mag_upper = k_mag + k_mag_error

        # Create an SED
        sed = ObservedSED()

        # FUV
        fuv = unitconversion.ab_to_jansky(fuv_mag)
        fuv_lower = unitconversion.ab_to_jansky(fuv_mag_upper)
        fuv_upper = unitconversion.ab_to_jansky(fuv_mag_lower)
        fuv_error = ErrorBar(fuv_lower, fuv_upper, at=fuv)
        sed.add_entry(self.filters["FUV"], fuv, fuv_error)

        # NUV
        nuv = unitconversion.ab_to_jansky(nuv_mag)
        nuv_lower = unitconversion.ab_to_jansky(nuv_mag_upper)
        nuv_upper = unitconversion.ab_to_jansky(nuv_mag_lower)
        nuv_error = ErrorBar(nuv_lower, nuv_upper, at=nuv)
        sed.add_entry(self.filters["NUV"], nuv, nuv_error)

        # u
        u = unitconversion.ab_to_jansky(u_mag)
        u_lower = unitconversion.ab_to_jansky(u_mag_upper)
        u_upper = unitconversion.ab_to_jansky(u_mag_lower)
        u_error = ErrorBar(u_lower, u_upper, at=u)
        sed.add_entry(self.filters["SDSS u"], u, u_error)

        # g
        g = unitconversion.ab_to_jansky(g_mag)
        g_lower = unitconversion.ab_to_jansky(g_mag_upper)
        g_upper = unitconversion.ab_to_jansky(g_mag_lower)
        g_error = ErrorBar(g_lower, g_upper, at=g)
        sed.add_entry(self.filters["SDSS g"], g, g_error)

        # r
        r = unitconversion.ab_to_jansky(r_mag)
        r_lower = unitconversion.ab_to_jansky(r_mag_upper)
        r_upper = unitconversion.ab_to_jansky(r_mag_lower)
        r_error = ErrorBar(r_lower, r_upper, at=r)
        sed.add_entry(self.filters["SDSS r"], r, r_error)

        # i
        i = unitconversion.ab_to_jansky(i_mag)
        i_lower = unitconversion.ab_to_jansky(i_mag_upper)
        i_upper = unitconversion.ab_to_jansky(i_mag_lower)
        i_error = ErrorBar(i_lower, i_upper, at=i)
        sed.add_entry(self.filters["SDSS i"], i, i_error)

        # z
        z = unitconversion.ab_to_jansky(z_mag)
        z_lower = unitconversion.ab_to_jansky(z_mag_upper)
        z_upper = unitconversion.ab_to_jansky(z_mag_lower)
        z_error = ErrorBar(z_lower, z_upper, at=z)
        sed.add_entry(self.filters["SDSS z"], z, z_error)

        # J
        j = unitconversion.ab_to_jansky(j_mag)
        j_lower = unitconversion.ab_to_jansky(j_mag_upper)
        j_upper = unitconversion.ab_to_jansky(j_mag_lower)
        j_error = ErrorBar(j_lower, j_upper, at=j)
        sed.add_entry(self.filters["J"], j, j_error)

        # H
        h = unitconversion.ab_to_jansky(h_mag)
        h_lower = unitconversion.ab_to_jansky(h_mag_upper)
        h_upper = unitconversion.ab_to_jansky(h_mag_lower)
        h_error = ErrorBar(h_lower, h_upper, at=h)
        sed.add_entry(self.filters["H"], h, h_error)

        # Ks
        k = unitconversion.ab_to_jansky(k_mag)
        k_lower = unitconversion.ab_to_jansky(k_mag_upper)
        k_upper = unitconversion.ab_to_jansky(k_mag_lower)
        k_error = ErrorBar(k_lower, k_upper, at=k)
        sed.add_entry(self.filters["K"], k, k_error)

        # Table7: IRAC and MIPS asymptotic magnitudes
        # - "logF3.6": Spitzer/IRAC 3.6um flux density [logJy]
        # - "e_logF3.6": logF3.6 uncertainty [logJy]
        # - "logF4.5": Spitzer/IRAC 4.5um flux density [logJy]
        # - "e_logF4.5": logF4.5 uncertainty [logJy]
        # - "logF5.8": Spitzer/IRAC 5.8um flux density [logJy]
        # - "e_logF5.8": logF5.8 uncertainty [logJy]
        # - "logF8.0": Spiter/IRAC 8.0um flux density [logJy]
        # - "e_logF8.0": logF8.0 uncertainty [logJy]
        # - "logF24": Spiter/MIPS 24um flux density [logJy]
        # - "e_logF24": logF24 uncertainty [logJy]
        # - "logF70": Spiter/MIPS 70um flux density [logJy]
        # - "e_logF70": logF70 uncertainty [logJy]
        # - "logF160": Spiter/MIPS 160um flux density [logJy]
        # - "e_logF160": logF160 uncertainty [logJy]

        # Table7 -> index 6

        galaxy_index = tables.find_index(result[6], self.ngc_id)

        # 3.6 micron
        i1_log = result[6][galaxy_index]["logF3.6"]
        i1_log_error = result[6][galaxy_index]["e_logF3.6"]
        i1_log_lower = i1_log - i1_log_error
        i1_log_upper = i1_log + i1_log_error

        # 4.5 micron
        i2_log = result[6][galaxy_index]["logF4.5"]
        i2_log_error = result[6][galaxy_index]["e_logF4.5"]
        i2_log_lower = i2_log - i2_log_error
        i2_log_upper = i2_log + i2_log_error

        # 5.8 micron
        i3_log = result[6][galaxy_index]["logF5.8"]
        i3_log_error = result[6][galaxy_index]["e_logF5.8"]
        i3_log_lower = i3_log - i3_log_error
        i3_log_upper = i3_log + i3_log_error

        # 8.0 micron
        i4_log = result[6][galaxy_index]["logF8.0"]
        i4_log_error = result[6][galaxy_index]["e_logF8.0"]
        i4_log_lower = i4_log - i4_log_error
        i4_log_upper = i4_log + i4_log_error

        #print("i4log", i4_log)
        #print("i4_log_error", i4_log_error)
        #print("i4_log_lower", i4_log_lower)
        #print("i4_log_upper", i4_log_upper)

        # 24 micron
        mips24_log = result[6][galaxy_index]["logF24"]
        mips24_log_error = result[6][galaxy_index]["e_logF24"]
        mips24_log_lower = mips24_log - mips24_log_error
        mips24_log_upper = mips24_log + mips24_log_error

        # 70 micron
        mips70_log = result[6][galaxy_index]["logF70"]
        mips70_log_error = result[6][galaxy_index]["e_logF70"]
        mips70_log_lower = mips70_log - mips70_log_error
        mips70_log_upper = mips70_log + mips70_log_error

        # 160 micron
        mips160_log = result[6][galaxy_index]["logF160"]
        mips160_log_error = result[6][galaxy_index]["e_logF160"]
        mips160_log_lower = mips160_log - mips160_log_error
        mips160_log_upper = mips160_log + mips160_log_error

        # Calculate data points and errobars in Janskys, add to the SED

        # 3.6 micron
        i1 = 10.**i1_log
        i1_lower = 10.**i1_log_lower
        i1_upper = 10.**i1_log_upper
        i1_error = ErrorBar(i1_lower, i1_upper, at=i1)
        sed.add_entry(self.filters["I1"], i1, i1_error)

        # 4.5 micron
        i2 = 10.**i2_log
        i2_lower = 10.**i2_log_lower
        i2_upper = 10.**i2_log_upper
        i2_error = ErrorBar(i2_lower, i2_upper, at=i2)
        sed.add_entry(self.filters["I2"], i2, i2_error)

        # 5.8 micron
        i3 = 10.**i3_log
        i3_lower = 10.**i3_log_lower
        i3_upper = 10.**i3_log_upper
        i3_error = ErrorBar(i3_lower, i3_upper, at=i3)
        sed.add_entry(self.filters["I3"], i3, i3_error)

        # 8.0 micron
        i4 = 10.**i4_log
        i4_lower = 10.**i4_log_lower
        i4_upper = 10.**i4_log_upper
        i4_error = ErrorBar(i4_lower, i4_upper, at=i4)
        sed.add_entry(self.filters["I4"], i4, i4_error)

        # 24 micron
        mips24 = 10.**mips24_log
        mips24_lower = 10.**mips24_log_lower
        mips24_upper = 10.**mips24_log_upper
        mips24_error = ErrorBar(mips24_lower, mips24_upper, at=mips24)
        sed.add_entry(self.filters["MIPS 24"], mips24, mips24_error)

        # 70 micron
        mips70 = 10.**mips70_log
        mips70_lower = 10.**mips70_log_lower
        mips70_upper = 10.**mips70_log_upper
        mips70_error = ErrorBar(mips70_lower, mips70_upper, at=mips70)
        sed.add_entry(self.filters["MIPS 70"], mips70, mips70_error)

        # 160 micron
        mips160 = 10.**mips160_log
        mips160_lower = 10.**mips160_log_lower
        mips160_upper = 10.**mips160_log_upper
        mips160_error = ErrorBar(mips160_lower, mips160_upper, at=mips160)
        sed.add_entry(self.filters["MIPS 160"], mips160, mips160_error)

        # Add the SED to the dictionary
        self.seds["SINGS"] = sed

    # -----------------------------------------------------------------

    def get_lvl(self):
        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Getting fluxes from the LVL catalog ...")

        # Create an SED
        sed = ObservedSED()

        # "J/MNRAS/445/881": LVL global optical photometry (Cook+, 2014)
        #  - "J/MNRAS/445/881/sample": Galaxies of the Spitzer Local Volume Legacy (LVL): properties (table1) and R25 photometry
        #  - "J/MNRAS/445/881/table3": Photometry within the IR apertures of Dale et al. (2009, Cat. J/ApJ/703/517) (258 rows)
        #  - "J/MNRAS/445/881/table4": Photometry within the UV apertures of Lee et al. (2011, Cat. J/ApJS/192/6) (258 rows)

        result = self.vizier.query_object(self.galaxy_name,
                                          catalog="J/MNRAS/445/881/sample")

        # ALL IN AB MAGNITUDE SYSTEM
        # Umag
        # Bmag
        # Vmag
        # Rmag
        # umag
        # gmag
        # rmag
        # imag
        # zmag

        # On SimBad, only sdss bands are used, from /sample ...

        relevant_bands = [("U", "U"), ("B", "B"), ("V", "V"), ("R", "R"),
                          ("u", "SDSS u"), ("g", "SDSS g"), ("r", "SDSS r"),
                          ("i", "SDSS i"), ("z", "SDSS z")]
        for band_prefix_catalog, filter_name in relevant_bands:

            column_name = band_prefix_catalog + "mag"
            error_column_name = "e_" + column_name

            # Skip masked values
            if result[0][column_name].mask[0]: continue

            # AB magnitude
            magnitude = result[0][0][column_name]
            magnitude_error = result[0][0][error_column_name]
            magnitude_lower = magnitude - magnitude_error
            magnitude_upper = magnitude + magnitude_error

            # Convert to Jy
            fluxdensity = unitconversion.ab_to_jansky(magnitude)
            fluxdensity_lower = unitconversion.ab_to_jansky(magnitude_upper)
            fluxdensity_upper = unitconversion.ab_to_jansky(magnitude_lower)
            fluxdensity_error = ErrorBar(fluxdensity_lower,
                                         fluxdensity_upper,
                                         at=fluxdensity)

            # Add data point to SED
            sed.add_entry(self.filters[filter_name], fluxdensity,
                          fluxdensity_error)

        # Add the SED to the dictionary
        self.seds["LVL"] = sed

    # -----------------------------------------------------------------

    def get_spitzer(self):
        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Getting fluxes from the Spitzer catalog ...")

        # "J/ApJ/703/517": The Spitzer Local Volume Legacy: IR photometry (Dale+, 2009)
        # "J/ApJ/703/517/sample": Galaxy sample (table 1) and infrared flux densities (table 2) (258 rows)

        # Create an SED
        sed = ObservedSED()

        result = self.vizier.query_object(self.galaxy_name,
                                          catalog="J/ApJ/703/517/sample")

        # F1.25: 2MASS J band (1.25 micron) flux density [Jy]
        # e_F1.25: Uncertainty in F1.25 [Jy]
        # F1.65: 2MASS H band (1.65 micron) flux density [Jy]
        # e_F1.65: Uncertainty in F1.65 [Jy]
        # F2.17: 2MASS Ks band (2.17 micron) flux density [Jy]
        # e_F2.17: Uncertainty in F2.17 [Jy]
        # F3.6: Spitzer/IRAC 3.5 micron band flux density [Jy]
        # e_F3.6: Uncertainty in F3.6 [Jy]
        # F4.5: Spitzer/IRAC 4.5 micron band flux density [Jy]
        # e_F4.5: Uncertainty in F4.5 [Jy]
        # F5.8: Spitzer/IRAC 5.8 micron band flux density [Jy]
        # e_F5.8: Uncertainty in F5.8 [Jy]
        # F8.0: Spitzer/IRAC 8.0 micron band flux density [Jy]
        # e_F8.0: Uncertainty in F8.0 [Jy]
        # F24: Spitzer/MIPS 24 micron flux density [Jy]
        # e_F24: Uncertainty in F24 [Jy]
        # F70: Spitzer/MIPS 70 micron band flux density [Jy]
        # e_F70: Uncertainty in F70 [Jy]
        # F160: Spitzer/MIPS 160 micron band flux density [Jy]
        # e_F160: Uncertainty in F160 [Jy]

        relevant_bands = [("1.25", "J"), ("1.65", "H"), ("2.17", "K"),
                          ("3.6", "I1"), ("4.5", "I2"), ("5.8", "I3"),
                          ("8.0", "I4"), ("24", "MIPS 24"), ("70", "MIPS 70"),
                          ("160", "MIPS 160")]
        for band_prefix_catalog, filter_name in relevant_bands:

            column_name = "F" + band_prefix_catalog
            error_column_name = "e_" + column_name

            # Skip masked values
            if result[0][column_name].mask[0]: continue

            # Flux and error already in Jy
            fluxdensity = result[0][0][column_name]
            fluxdensity_error = ErrorBar(result[0][0][error_column_name])

            # Add data point to SED
            sed.add_entry(self.filters[filter_name], fluxdensity,
                          fluxdensity_error)

        # Add the SED to the dictionary
        self.seds["Spitzer"] = sed

    # -----------------------------------------------------------------

    def get_spitzer_irs(self):
        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Getting fluxes from the Spitzer/IRS catalog ...")

        # "J/MNRAS/414/500": Spitzer/IRS ATLAS project source (Hernan-Caballero+, 2011)
        # - "J/MNRAS/414/500/catalog": Spitzer/IRS ATLAS project source catalog, version 1.0 (739 rows)

        # !! Parentheses () are converted into underscores _ in the resulting Astropy tables!!

        # F(3.6): IRAC1 (3.6um) flux density [Jy]
        # e_F(3.6): rms uncertainty on F(3.6) [Jy]
        # F(8.0): IRAC4 (8.0um) flux density [Jy]
        # e_F(8.0): rms uncertainty on F(8.0) [Jy]
        # F(24): 24um flux density [Jy]
        # e_F(24): rms uncertainty on F(24) [Jy]
        # F(70): 70um or similar band flux density [Jy]
        # e_F(70): rms uncertainty on F(70) [Jy]

        # Create an SED
        sed = ObservedSED()

        result = self.vizier.query_object(self.galaxy_name,
                                          catalog="J/MNRAS/414/500/catalog")

        relevant_bands = [("3.6", "I1"), ("8.0", "I4"), ("24", "MIPS 24"),
                          ("70", "MIPS 70")]
        for band_prefix_catalog, filter_name in relevant_bands:

            column_name = "F_" + band_prefix_catalog + "_"
            error_column_name = "e_" + column_name

            # Skip masked values
            if result[0][column_name].mask[0]: continue

            # Flux and error already in Jy
            fluxdensity = result[0][0][column_name]
            fluxdensity_error = ErrorBar(result[0][0][error_column_name])

            # Add data point to SED
            sed.add_entry(self.filters[filter_name], fluxdensity,
                          fluxdensity_error)

        # Add the SED to the dictionary
        self.seds["Spitzer-IRS"] = sed

    # -----------------------------------------------------------------

    def get_iras(self):
        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Getting fluxes from the IRAS catalog ...")

        # "J/ApJS/178/280": Compendium of ISO far-IR extragalactic data (Brauher+, 2008)
        # - "J/ApJS/178/280/table1": *Galaxies and properties

        # F12: IRAS 12um band flux density [Jy]
        # F25: IRAS 25um band flux density [Jy]
        # F60: IRAS 60um band flux density [Jy]
        # F100: IRAS 100um band flux density [Jy]

        # No errors ...

        # Create an SED
        sed = ObservedSED()

        result = self.vizier.query_object(self.galaxy_name,
                                          catalog="J/ApJS/178/280/table1")

        relevant_bands = [("12", "IRAS 12"), ("25", "IRAS 25"),
                          ("60", "IRAS 60"), ("100", "IRAS 100")]
        for band_prefix_catalog, filter_name in relevant_bands:

            column_name = "F" + band_prefix_catalog

            # Skip masked values
            if result[0][column_name].mask[0]: continue

            # Flux and error already in Jy
            fluxdensity = result[0][0][column_name]
            fluxdensity_error = ErrorBar(0.0)

            # Add data point to SED
            sed.add_entry(self.filters[filter_name], fluxdensity,
                          fluxdensity_error)

        # Add the SED to the dictionary
        self.seds["IRAS"] = sed

    # -----------------------------------------------------------------

    def get_iras_fsc(self):
        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Getting fluxes from the IRAS-FSC catalog ...")

        # "J/MNRAS/398/109": Imperial IRAS-FSC redshift catalogue (IIFSCz) (Wang+, 2009)
        # - "J/MNRAS/398/109/iifsczv4": IIFSCz Catalogue (MRR+LW 18/04/09)[spectrum/SED] (60303 rows)

        # S12um: IRAS-FSC flux at 12um [Jy]
        # S25um: IRAS-FSC flux at 25um [Jy]
        # S60um: IRAS-FSC flux at 60um [Jy]
        # S100um: IRAS-FSC flux at 100um [Jy]

        # not used in Simbad:
        # umag: SDSS u magnitude [mag: which system??]
        # e_umag:
        # gmag:
        # e_gmag:
        # rmag:
        # e_rmag:
        # imag:
        # e_imag:
        # zmag:
        # e_zmag:

        # Jmag: 2MASS J magnitude [mag: which system??]
        # e_Jmag: rms uncertainty on Jmag [mag: which system??]
        # Hmag: 2MASS H magnitude [mag]
        # e_Hmag: rms uncertainty on Hmag [mag]
        # Kmag: 2MASS K magnitude [mag]
        # e_Kmag rms uncertainty on Kmag [mag]

        # Create an SED
        sed = ObservedSED()

        result = self.vizier.query_object(self.galaxy_name,
                                          catalog="J/MNRAS/398/109/iifsczv4")

        relevant_bands = [("12", "IRAS 12"), ("25", "IRAS 25"),
                          ("60", "IRAS 60"), ("100", "IRAS 100")]
        for band_prefix_catalog, filter_name in relevant_bands:

            # Flux and error already in Jy
            fluxdensity = result[0][0]["S" + band_prefix_catalog + "um"]
            fluxdensity_error = ErrorBar(0.0)

            # Add data point to SED
            sed.add_entry(self.filters[filter_name], fluxdensity,
                          fluxdensity_error)

        # Add the SED to the dictionary
        self.seds["IRAS-FSC"] = sed

    # -----------------------------------------------------------------

    def get_s4g(self):
        """
        This function ...
        :return:
        """

        # Create an SED
        sed = ObservedSED()

        # Get parameters from S4G catalog
        result = self.vizier.query_object(self.galaxy_name,
                                          catalog=["J/PASP/122/1397/s4g"])
        table = result[0]

        # Magnitudes
        i1_mag = table["__3.6_"][0]
        i2_mag = table["__4.5_"][0]
        i1_mag_error = table["e__3.6_"][0]
        i2_mag_error = table["e__4.5_"][0]

        i1_fluxdensity = unitconversion.ab_to_jansky(i1_mag)
        i1_fluxdensity_lower = unitconversion.ab_to_jansky(i1_mag +
                                                           i1_mag_error)
        i1_fluxdensity_upper = unitconversion.ab_to_jansky(i1_mag -
                                                           i1_mag_error)
        i1_error = ErrorBar(i1_fluxdensity_lower,
                            i1_fluxdensity_upper,
                            at=i1_fluxdensity)

        # Add data point to SED
        sed.add_entry(self.filters["I1"], i1_fluxdensity, i1_error)

        i2_fluxdensity = unitconversion.ab_to_jansky(i2_mag)
        i2_fluxdensity_lower = unitconversion.ab_to_jansky(i2_mag +
                                                           i2_mag_error)
        i2_fluxdensity_upper = unitconversion.ab_to_jansky(i2_mag -
                                                           i2_mag_error)
        i2_error = ErrorBar(i2_fluxdensity_lower,
                            i2_fluxdensity_upper,
                            at=i2_fluxdensity)

        # Add data point to SED
        sed.add_entry(self.filters["I2"], i2_fluxdensity, i2_error)

        # Add the SED to the dictionary
        self.seds["S4G"] = sed

    # -----------------------------------------------------------------

    def get_brown(self):
        """
        This function ...
        :return:
        """

        # J/ApJS/212/18/sample
        # AB magnitudes for the sample with neither foreground nor intrinsic dust extinction corrections, and modeled Milky Way foreground dust extinction

        # Create an SED
        sed = ObservedSED()

        # FUV: [12.5/22.9] GALEX FUV AB band magnitude
        # e_FUV:
        # UVW2:
        # e_UVW2:
        # UVM2:
        # e_UVM2:
        # NUV:
        # e_NUV:
        # UVW1:
        # e_UVW1:
        # Umag: [11.9/15.7] Swift/UVOT U AB band magnitude
        # e_Umag:
        # umag:
        # e_umag:
        # gmag:
        # e_gmag:
        # Vmag:
        # e_Vmag:
        # rmag:
        # e_rmag:
        # imag:
        # e_imag:
        # zmag:
        # e_zmag:
        # Jmag:
        # e_Jmag:
        # Hmag:
        # e_Hmag:
        # Ksmag:
        # e_Ksmag:
        # W1mag:
        # e_W1mag:
        # [3.6]:
        # e_[3.6]:
        # [4.5]:
        # e_[4.5]:
        # W2mag:
        # e_W2mag:
        # [5.8]:
        # e_[5.8]:
        # [8.0]:
        # e_[8.0]:
        # W3mag:
        # e_W3mag:
        # W4mag:
        # e_W4mag:
        # W4'mag: Corrected WISE W4 AB band magnitude
        # e_W4'mag:
        # [24]:
        # e_[24]:

        pass

    # -----------------------------------------------------------------

    def get_planck(self):
        """
        This function ...
        :return:
        """

        # Create an SED
        sed = ObservedSED()

        # The second release is not yet available ... ??

    # -----------------------------------------------------------------

    def get_emission_lines(self):
        """
        This function ...
        :return:
        """

        # Create an SED
        sed = ObservedSED()

        # J/ApJS/190/233/Opt

        # Get result
        result = self.vizier.get_catalogs("J/ApJS/190/233/Opt")
        table = result[0]

        galaxy_index = tables.find_index(table, self.ngc_id, "Name")

        # FHa: The Hα 6563 Angstrom line flux (aW/m2)
        # e_FHa: Uncertainty in Ha (aW/m2)
        # FNII: The [NII] 6584Å line flux (aW/m2)
        # e_FNII: Uncertainty in NII (aW/m2)

        # H alpha
        ha_flux = table["FHa"][galaxy_index] * Unit("aW/m2")
        ha_flux_error = table["e_FHa"][galaxy_index] * Unit("aW/m2")

        # NII
        n2_flux = table["FNII"][galaxy_index] * Unit("aW/m2")
        n2_flux_error = table["e_FNII"][galaxy_index] * Unit("aW/m2")

        ha_filter = self.filters["Ha"]
        ha_wavelength = ha_filter.centerwavelength() * Unit("micron")
        ha_frequency = ha_wavelength.to("Hz", equivalencies=spectral())

        # Calculate flux density
        ha_fluxdensity = (ha_flux / ha_frequency).to("Jy")
        ha_fluxdensity_error = (ha_flux_error / ha_frequency).to("Jy")
        ha_errorbar = ErrorBar(ha_fluxdensity_error)

        # Add entry
        sed.add_entry(ha_filter, ha_fluxdensity, ha_errorbar)

        # Add the SED to the dictionary
        self.seds["Lines"] = sed

    # -----------------------------------------------------------------

    def get_m81(self):
        """
        This function ...
        :return:
        """

        # UV through far-IR analysis of M81 (Perez-Gonzalez+, 2006)
        # "J/ApJ/648/987"
        # Two tables:
        # - "J/ApJ/648/987/table1": Positions and photometry (GLOBALBKG and LOCALBKG cases) for the regions selected at 160um resolution
        # - "J/ApJ/648/987/table2": Positions and photometry for the regions selected at 24um resolution

        # Table 1

        #result = self.vizier.query_object(self.galaxy_name, catalog="J/ApJ/648/987/table1")

        # Looks like this (only 3 matches)
        #1	M81	09 55 32.2	+69 03 59.0	680.0	40.78	43.18	42.84	42.63	42.61	41.98	42.67	42.99
        #2	Reg02	09 55 32.2	+69 03 59.0	64.0	39.67	42.63	42.28	41.98	41.76	41.25	41.83	41.78
        #3	Reg03	09 55 32.2	+69 03 59.0	104.0	39.40	42.38	42.03	41.76	41.58	40.90	41.62	41.78

        #galaxy_index = tables.find_index(result, self.galaxy_name)

        # Table 2

        # Interesting rows:
        # - logLFUV: Log of the FUV luminosity [1e-7 W] or [erg/s]
        # - logLNUV: Log of the NUV luminosity [1e-7 W] or [erg/s]
        # - logLHa: Log of the H-alpha luminosity [1e-7 W] or [erg/s]
        # - logL8: Log of the 8um luminosity [1e-7 W] or [erg/s]
        # - logL24: Log of the 24um luminosity [1e-7 W] or [erg/s]

        result = self.vizier.query_object(self.galaxy_name,
                                          catalog="J/ApJ/648/987/table2")

        galaxy_index = tables.find_index(result[0], self.galaxy_name)

        # Create an SED
        sed = ObservedSED()

        relevant_bands = [("FUV", "FUV"), ("NUV", "NUV"), ("Ha", "Ha"),
                          ("8", "I4"), ("24", "MIPS 24")]
        for band_prefix_catalog, filter_name in relevant_bands:

            # Flux and error already in Jy
            log = result[0][galaxy_index]["logL" + band_prefix_catalog]
            flux = 10.**log  # In erg/s

            frequency = 0.0  # TODO: calculate this!

            # Also: calculate the amount of flux per unit area ! : divide also by 4 pi d_galaxy**2 !

            fluxdensity = flux / frequency
            fluxdensity_error = ErrorBar(0.0)

            # Add data point to SED
            sed.add_entry(self.filters[filter_name], fluxdensity,
                          fluxdensity_error)

        # Add the SED to the dictionary
        self.seds["IRAS-FSC"] = sed

    # -----------------------------------------------------------------

    def write(self):
        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Writing ...")

        # If requested, write out the SEDs
        if self.config.write_seds: self.write_seds()

    # -----------------------------------------------------------------

    def write_seds(self):
        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Writing the SEDs ...")

        # Determine the full path to the SEDs directory
        path = self.full_output_path(self.config.writing.seds_path)

        # Create the SEDs directory if necessary
        fs.create_directory(path)

        # Loop over the different SEDs
        for label in self.seds:

            # Debugging info
            log.debug("Writing SED from " + label)

            # Determine the path to the new SED file
            sed_path = fs.join(path, label + ".dat")

            # Save the SED at the specified location
            self.seds[label].save(sed_path)
def vizier_query(object, params=None, method="both", coordinate=False):
    """Give mean/median values of some parameters for an object.
    This script use VizieR for looking up the object.

    :object: The object to query (e.g. HD20010).
    :parama: Extra parameters to look for (default is Teff, logg, __Fe_H_).
    :method: Print median, main or both

    :returns: A dictionary with the parameters

    """

    methods = ("median", "mean", "both")
    if method not in methods:
        raise ValueError("method must be one of:", methods)

    with warnings.catch_warnings():
        warnings.simplefilter("ignore")
        cat = Vizier.query_object(object)

    if coordinate:
        for c in cat:
            try:
                ra = c["RAJ2000"][0]
                dec = c["DEJ2000"][0]
            except KeyError:
                ra = 0
                pass
            if ra != 0:
                break
        print("%s %s %s" % (object, ra, dec))
    else:
        print("Object: %s" % object)

    parameters = {"Teff": [], "logg": [], "__Fe_H_": []}
    if params:
        params = ["Teff", "logg", "__Fe_H_"]
        for param in params:
            parameters[param] = []

        for ci in cat:
            for column in parameters.keys():
                try:
                    parameters[column].append(ci[column].quantity)
                except (TypeError, KeyError):
                    pass

        for key in parameters.keys():
            pi = parameters[key]
            parameters[key] = _q2a(pi)
            if len(parameters[key]):
                mean = round(np.nanmean(parameters[key]), 2)
                median = round(np.nanmedian(parameters[key]), 2)
            else:
                mean = "Not available"
                median = "Not available"

            if key.startswith("__"):
                key = "[Fe/H]"
            if method == "mean":
                print("\n%s:\tMean value: %s" % (key, mean))
            elif method == "median":
                print("\n%s\tMedian value: %s" % (key, median))
            else:
                print("\n%s:\tMean value: %s" % (key, mean))
                print("%s:\tMedian value: %s" % (key, median))

    return parameters, cat
Exemple #38
0
Fichier : s4g.py Projet : SKIRT/PTS
class S4G(Configurable):

    """
    This class ...
    """

    def __init__(self, *args, **kwargs):

        """
        The constructor ...
        :param kwargs:
        """

        # Call the constructor of the base class
        super(S4G, self).__init__(*args, **kwargs)

        # The inclination
        self.inclination = None

        # The galaxy properties object
        self.properties = None

        # The dictionary of components
        self.components = dict()

        # The Vizier service
        self.vizier = None

    # -----------------------------------------------------------------

    def _run(self, **kwargs):

        """
        This function ...
        :return:
        """

        # 2. Get galaxy properties
        self.get_properties()

        # 3. Get the components
        self.get_components()

        # 4. Show
        if self.config.show: self.show()

        # 5. Writing
        if self.config.write: self.write()

    # -----------------------------------------------------------------

    def setup(self, **kwargs):

        """
        This function ...
        :param kwargs:
        :return:
        """

        # Call the setup function of the base class
        super(S4G, self).setup(**kwargs)

        # Get the inclination
        self.inclination = kwargs.pop("inclination", None)

        # Create the galaxy properties object
        self.properties = GalaxyProperties()
        self.properties.name = self.config.galaxy_name

        # Get the NGC name of the galaxy
        self.properties.ngc_name = self.ngc_name

    # -----------------------------------------------------------------

    @lazyproperty
    def ngc_name(self):

        """
        This function ...
        :return:
        """

        return catalogs.get_ngc_name(self.config.galaxy_name)

    # -----------------------------------------------------------------

    @property
    def ngc_name_nospaces(self):

        """
        This function ...
        :return:
        """

        return self.ngc_name.replace(" ", "")

    # -----------------------------------------------------------------

    def get_properties(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Getting the galaxy properties ...")

        # S4G
        self.get_s4g_properties()

        # To first order, cos i = b/a where a & b
        # are the observed major and minor
        # axes (assume disk is intrinsically
        # circular)

        # But this implies galaxies have zero
        # thickness at i = 90°! Better to assume
        # that spirals are oblate ellipsoids with
        # intrinsic axis ratios a:a:c. If q = c/a,
        # then after a bit a simple geometry

        # cos^2(i) = [ (b/a)^2 - q^2 ] / (1-q^2)


        # The best fit to observed spirals yields
        # q = 0.13 for Sc galaxies

        # Ellipticity (1-b/a)

        # b_to_a = 1. - self.properties.ellipticity
        #
        # # Calculate the inclination
        # inclination_deg = 90. - math.degrees(math.acos(b_to_a))
        # inclination = Angle(inclination_deg, "deg")
        #
        # # Check the inclination
        # if self.inclination is not None:
        #     difference = abs(self.inclination - inclination)
        #     rel_difference = difference / self.inclination
        #     if rel_difference > 0.1:
        #         log.warning("The inclination angle calculated based on the decomposition differs by " + str(rel_difference*100) + "% from the specified inclination")
        #
        # # Set the incliantion
        # self.properties.inclination = inclination

    # -----------------------------------------------------------------

    def get_s4g_properties(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Querying the S4G catalog ...")

        # The Vizier querying object, specifying the necessary columns for this class
        self.vizier = Vizier(columns=['Name', 'RAJ2000', 'DEJ2000', 'Dmean', 'e_Dmean', 'amaj', 'ell', '[3.6]', '[4.5]', 'e_[3.6]', 'e_[4.5]', 'PA'])
        self.vizier.ROW_LIMIT = -1

        # Get parameters from S4G catalog
        result = self.vizier.query_object(self.config.galaxy_name, catalog=["J/PASP/122/1397/s4g"])
        table = result[0]

        # Galaxy name for S4G catalog
        self.properties.name = table["Name"][0]
        # Galaxy center from decomposition (?)
        ra_center = table["RAJ2000"][0]
        dec_center = table["DEJ2000"][0]
        center = SkyCoordinate(ra=ra_center, dec=dec_center, unit="deg", frame='fk5')
        self.properties.center = center

        # Center position
        #self.properties.center = SkyCoordinate(ra=self.info["RA"][0], dec=self.info["DEC"][0], unit="deg") # center position from DustPedia
        
        # Distance
        self.properties.distance = table["Dmean"][0] * u("Mpc")
        self.properties.distance_error = table["e_Dmean"][0] * u("Mpc")

        # Major axis, ellipticity, position angle
        self.properties.major_arcsec = table["amaj"][0] * u("arcsec")
        self.properties.major = (self.properties.distance * self.properties.major_arcsec).to("pc", equivalencies=dimensionless_angles())

        # Ellipticity
        self.properties.ellipticity = table["ell"][0]
        self.properties.position_angle = Angle(table["PA"][0] + 90.0, u("deg"))

        # Magnitudes
        asymptotic_ab_magnitude_i1 = table["__3.6_"][0]
        asymptotic_ab_magnitude_i2 = table["__4.5_"][0]
        asymptotic_ab_magnitude_i1_error = table["e__3.6_"][0]
        asymptotic_ab_magnitude_i2_error = table["e__4.5_"][0]

        #self.properties.i1_mag = asymptotic_ab_magnitude_i1
        #self.properties.i1_mag_error = asymptotic_ab_magnitude_i1_error
        #self.properties.i2_mag = asymptotic_ab_magnitude_i2
        #self.properties.i2_mag_error = asymptotic_ab_magnitude_i2_error

        #self.properties.i1_fluxdensity = unitconversion.ab_to_jansky(self.properties.i1_mag) * Unit("Jy")
        #i1_fluxdensity_lower = unitconversion.ab_to_jansky(
        #    self.properties.i1_mag + self.properties.i1_mag_error) * Unit("Jy")
        #i1_fluxdensity_upper = unitconversion.ab_to_jansky(
        #    self.properties.i1_mag - self.properties.i1_mag_error) * Unit("Jy")
        #i1_error = ErrorBar(i1_fluxdensity_lower, i1_fluxdensity_upper, at=self.properties.i1_fluxdensity)
        #self.properties.i1_error = i1_error.average

        #self.properties.i2_fluxdensity = unitconversion.ab_to_jansky(self.properties.i2_mag) * Unit("Jy")
        #i2_fluxdensity_lower = unitconversion.ab_to_jansky(
        #    self.properties.i2_mag + self.properties.i2_mag_error) * Unit("Jy")
        #i2_fluxdensity_upper = unitconversion.ab_to_jansky(
        #    self.properties.i2_mag - self.properties.i2_mag_error) * Unit("Jy")
        #i2_error = ErrorBar(i2_fluxdensity_lower, i2_fluxdensity_upper, at=self.properties.i2_fluxdensity)
        #self.properties.i2_error = i2_error.average

        # Other ...
        # absolute_magnitude_i1 = table["M3.6"][0]
        # absolute_magnitude_i2 = table["M4.5"][0]
        # stellar_mass = 10.0**table["logM_"][0] * u.Unit("Msun")

    # -----------------------------------------------------------------

    def get_components(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Getting the components ...")

        # self.get_p4()  # currently (writing on the 31th of march 2016) there is a problem with the effective radius values
        # (at least for M81) on Vizier as well as in the PDF version of table 7 (S4G models homepage).

        # Parse the S4G table 8 to get the decomposition parameters
        self.get_parameters_from_table()

    # -----------------------------------------------------------------

    def get_p4(self):

        """
        This function ...
        :return:
        """

        #http://vizier.cfa.harvard.edu/viz-bin/VizieR?-source=J/ApJS/219/4

        # J/ApJS/219/4: S4G pipeline 4: multi-component decompositions (Salo+, 2015)

        #  - J/ApJS/219/4/galaxies: Parameters of the galaxies; center, outer orientation, sky background; and 1-component Sersic fits (tables 1 and 6) (2352 rows)
        #  - J/ApJS/219/4/table7: *Parameters of final multicomponent decompositions (Note) (4629 rows)

        # Inform the user
        log.info("Querying the S4G pipeline 4 catalog ...")

        # Get the "galaxies" table
        result = self.vizier.query_object(self.config.galaxy_name, catalog=["J/ApJS/219/4/galaxies"])
        table = result[0]

        # PA: [0.2/180] Outer isophote position angle
        # e_PA: [0/63] Standard deviation in PA
        # Ell:  [0.008/1] Outer isophote ellipticity
        # e_Ell: [0/0.3] Standard deviation in Ell

        pa = Angle(table["PA"][0] - 90., "deg")
        pa_error = Angle(table["e_PA"][0], "deg")

        ellipticity = table["Ell"][0]
        ellipticity_error = table["e_Ell"][0]

        # Get the "table7" table
        result = self.vizier.get_catalogs("J/ApJS/219/4/table7")
        table = result[0]

        # Name: Galaxy name
        # Mod: Type of final decomposition model
        # Nc: [1/4] Number of components in the model (1-4)
        # Q: [3/5] Quality flag, 5=most reliable
        # C: Physical interpretation of the component
        # Fn: The GALFIT function used for the component (sersic, edgedisk, expdisk, ferrer2 or psf)
        # f1: [0.006/1] "sersic" fraction of the total model flux
        # mag1:  [7/19.4] "sersic" total 3.6um AB magnitude
        # q1: [0.1/1] "sersic" axis ratio
        # PA1: [0.08/180] "sersic" position angle [deg]
        # Re: [0.004/430] "sersic" effective radius (Re) [arcsec]
        # n: [0.01/20] "sersic" parameter n
        # f2: [0.02/1] "edgedisk" fraction of the total model flux
        # mu02: [11.8/24.6] "edgedisk" central surface face-on brightness (µ0) [mag/arcsec2]
        # PA2: [-90/90] "edgedisk" PA [deg]
        # hr2: [1/153] "edgedisk" exponential scale length (hr) [arcsec]
        # hz2: [0.003/39] "edgedisk" z-scale hz [arcsec]
        # f3: [0.02/1] "expdisk" fraction of the total model flux
        # mag3: [6.5/18.1] "expdisk" total 3.6um AB magnitude [mag]
        # q3: [0.1/1] "expdisk" axis ratio
        # PA3: [-90/90] "expdisk" position angle [deg]
        # hr3: [0.7/332] "expdisk" exponential scale length (hr) [arcsec]
        # mu03: [16.4/25.3] "expdisk" central surface face-on brightness (µ0) [mag/arcsec2]
        # f4: [0.003/0.6] "ferrer2" fraction of the total model flux
        # mu04: [16/24.8] "ferrer2" central surface sky brightness (µ0) [mag/arcsec2]
        # q4: [0.01/1] "ferrer2" axis ratio
        # PA4: [-90/90] "ferrer2" position angle [deg]
        # Rbar: [3.7/232.5] "ferrer2" outer truncation radius of the bar (Rbar) [arcsec]
        # f5: [0.001/0.4] "psf" fraction of the total model flux
        # mag5: [11.5/21.1] "psf" total 3.6um AB magnitude [mag]

        indices = tables.find_indices(table, self.ngc_name_nospaces, "Name")

        labels = {"sersic": 1, "edgedisk": 2, "expdisk": 3, "ferrer2": 4, "psf": 5}

        #units = {"f": None, "mag": "mag", "q": None, "PA": "deg", }

        # Loop over the indices
        for index in indices:

            model_type = table["Mod"][index]
            number_of_components = table["Nc"][index]
            quality = table["Q"][index]
            interpretation = table["C"][index]
            functionname = table["Fn"][index]

            component_parameters = Map()

            if self.parameters.model_type is not None: assert model_type == self.parameters.model_type
            if self.parameters.number_of_components is not None: assert number_of_components == self.parameters.number_of_components
            if self.parameters.quality is not None: assert quality == self.parameters.quality
            self.parameters.model_type = model_type
            self.parameters.number_of_components = number_of_components
            self.parameters.quality = quality

            for key in table.colnames:

                if not key.endswith(str(labels[functionname])): continue

                parameter = key[:-1]

                value = table[key][index]

                if parameter == "PA":
                    value = Angle(value + 90., "deg")
                    if quadrant(value) == 2: value = value - Angle(180., "deg")
                    elif quadrant(value) == 3: value = value + Angle(180., "deg")

                    if value.to("deg").value > 180.: value = value - Angle(360., "deg")
                    elif value.to("deg").value < -180.: value = value + Angle(360., "deg")
                elif parameter == "mag":
                    parameter = "fluxdensity"
                    value = unitconversion.ab_to_jansky(value) * u("Jy")
                elif parameter == "mu0": value = value * u("mag/arcsec2")
                elif parameter == "hr":
                    value = value * u("arcsec")
                    value = (self.parameters.distance * value).to("pc", equivalencies=dimensionless_angles())
                elif parameter == "hz":
                    value = value * u("arcsec")
                    value = (self.parameters.distance * value).to("pc", equivalencies=dimensionless_angles())

                component_parameters[parameter] = value

            if functionname == "sersic":

                re = table["Re"][index] * u("arcsec")
                component_parameters["Re"] = (self.parameters.distance * re).to("pc", equivalencies=dimensionless_angles())
                component_parameters["n"] = table["n"][index]

            elif functionname == "ferrer2":

                rbar = table["Rbar"][index] * u("arcsec")
                component_parameters["Rbar"] = (self.parameters.distance * rbar).to("pc", equivalencies=dimensionless_angles())

            if interpretation == "B": # bulge

                self.parameters.bulge = component_parameters

            elif interpretation == "D": # disk

                self.parameters.disk = component_parameters

            else: raise RuntimeError("Unrecognized component: " + interpretation)

        # Determine the full path to the parameters file
        #path = fs.join(self.components_path, "parameters.dat")

        # Write the parameters to the specified location
        #write_parameters(self.parameters, path)

    # -----------------------------------------------------------------

    def get_parameters_from_table(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Getting the structural galaxy parameters from the S4G catalog ...")

        # Inform the user
        log.info("Parsing S4G table 8 to get the decomposition parameters ...")

        #The table columns are:
        # (1) the running number (1-2352),
        # (2) the galaxy name,
        # (3) the type of final decomposition model,
        # (4) the number N of components in the final model , and
        # (5) the quality flag Q.

        # 6- 8 for unresolved central component ('psf'; phys, frel,  mag),
        # 9-15 for inner sersic-component ('sersic1'; phys,  frel,  mag,    re,    ar,      pa,    n),
        # 16-22 for inner disk-component ('expo1'; phys,  frel,  mag,    hr,    ar,      pa,   mu0),
        # 23-28 for inner ferrers-component ('ferrers1'; phys,  frel,  mu0,   rout,   ar,     pa),
        # 29-34 for inner edge-on disk component ('edgedisk1';  phys, frel, mu0,  rs,    hs,      pa ).
        # 35-41 for outer sersic-component ('sersic2'; phys,  frel,  mag,    re,    ar,      pa,    n),
        # 42-49 for outer disk-component ('expo2'; phys,  frel,  mag,    hr,    ar,      pa,   mu0),
        # 50-55 for outer ferrers-component ('ferrers2'; phys,  frel,  mu0,   rout,   ar,     pa),
        # 56-61 for outer edge-on disk component ('edgedisk2';  phys, frel, mu0,  rs,    hs,      pa ).

        sersic_1_index = 8
        disk_1_index = 15

        edgedisk_1_index = 28

        #For each function:

        #the first entry stands for the physical intepretation of the component:
        #'N' for a central source (or unresolved bulge), 'B' for a bulge (or elliptical), 'D' for a disk, 'BAR' for a bar, and 'Z' for an edge-on disk.

        # 'rel    =  the relative contribution of the component to the total model flux,
        #  mag    =  the component's total 3.6 micron AB magnitude,
        #  mu0    =  the central  surface brightness (mag/arcsec^2;  de-projected central surface brightness for expdisk and edgedisk, and
        #                                                          sky-plane central surface brightness for ferrer)
        #  ar     =  axial ratio
        #  pa     =  position angle (degrees ccw from North)
        #  n      =  sersic index
        #  hr     =  exponential scale lenght (arcsec)
        #  rs     =  radial scale lenght (arcsec)
        #  hs     =  vertical scale height (arcsec)
        #  rout   =  bar outer truncation radius (arcsec)

        # SERSIC: phys,  frel,  mag,    re,    ar,      pa,    n
        # DISK: phys,  frel,  mag,    hr,    ar,      pa,   mu0

        # EDGEDISK: phys frel mu0    rs    hs      pa

        with open(local_table_path, 'r') as s4g_table:

            for line in s4g_table:

                splitted = line.split()

                if len(splitted) < 2: continue

                name = splitted[1]

                #print(list(name))

                # Only look at the line corresponding to the galaxy
                if name != self.ngc_name_nospaces: continue

                #if self.ngc_name_nospaces not in name: continue

                #self.parameters.model_type = splitted[2]
                #self.parameters.number_of_components = splitted[3]
                #self.parameters.quality = splitted[4]

                ## BULGE

                #self.parameters.bulge.interpretation = splitted[sersic_1_index].split("|")[1]
                bulge_f = float(splitted[sersic_1_index + 1])
                mag = float(splitted[sersic_1_index + 2])
                bulge_fluxdensity = unitconversion.ab_to_jansky(mag) * u("Jy")

                # Effective radius in pc
                re_arcsec = float(splitted[sersic_1_index + 3]) * u("arcsec")
                bulge_re = (self.properties.distance * re_arcsec).to("pc", equivalencies=dimensionless_angles())

                bulge_q = float(splitted[sersic_1_index + 4])
                bulge_pa = Angle(float(splitted[sersic_1_index + 5]) - 90., "deg")
                bulge_n = float(splitted[sersic_1_index + 6])

                # Create the bulge component
                bulge = SersicModel2D(rel_contribution=bulge_f, fluxdensity=bulge_fluxdensity, effective_radius=bulge_re,
                                      axial_ratio=bulge_q, position_angle=bulge_pa, index=bulge_n)

                # Add the bulge to the components dictionary
                self.components["bulge"] = bulge

                ## DISK

                if splitted[disk_1_index + 1] != "-":

                    #self.parameters.disk.interpretation = splitted[disk_1_index].split("|")[1]
                    disk_f = float(splitted[disk_1_index + 1])
                    mag = float(splitted[disk_1_index + 2])
                    disk_fluxdensity = unitconversion.ab_to_jansky(mag) * u("Jy")

                    # Scale length in pc
                    hr_arcsec = float(splitted[disk_1_index + 3]) * u("arcsec")
                    disk_hr = (self.properties.distance * hr_arcsec).to("pc", equivalencies=dimensionless_angles())

                    disk_q = float(splitted[disk_1_index + 4]) # axial ratio
                    disk_pa = Angle(float(splitted[disk_1_index + 5]) - 90., "deg")
                    disk_mu0 = float(splitted[disk_1_index + 6]) * u("mag/arcsec2")

                    # Create the disk component
                    disk = ExponentialDiskModel2D(rel_contribution=disk_f, fluxdensity=disk_fluxdensity, scalelength=disk_hr,
                                                  axial_ratio=disk_q, position_angle=disk_pa, mu0=disk_mu0)

                else:

                    # phys frel mu0    rs    hs      pa

                    disk_f = float(splitted[edgedisk_1_index + 1])
                    mag = None
                    fluxdensity = None

                    # frel mu0    rs    hs      pa

                    #print(disk_f)

                    disk_mu0 = float(splitted[edgedisk_1_index + 2]) * u("mag/arcsec2")

                    #  rs    hs      pa

                    #  rs     =  radial scale lenght (arcsec)
                    #  hs     =  vertical scale height (arcsec)

                    rs = float(splitted[edgedisk_1_index + 3])
                    hs = float(splitted[edgedisk_1_index + 4])

                    #print(rs)
                    #print(hs)

                    disk_pa = Angle(float(splitted[edgedisk_1_index + 5]) - 90., "deg")

                    #print(disk_pa)

                    disk_q = hs / rs

                    # Create the disk component
                    disk = ExponentialDiskModel2D(rel_contribution=disk_f, scalelength=rs, axial_ratio=disk_q, position_angle=disk_pa, mu0=disk_mu0)

                # Add the disk to the components dictionary
                self.components["disk"] = disk

    # -----------------------------------------------------------------

    @property
    def disk_pa(self):

        """
        This function ...
        :return:
        """

        return self.components["disk"].position_angle

    # -----------------------------------------------------------------

    def show(self):

        """
        This function ...
        :return:
        """

        print(fmt.green + "Galaxy properties" + fmt.reset + ":")
        print("")
        print(self.properties)
        print("")

        for name in self.components:

            print(fmt.green + name + fmt.reset + ":")
            print("")
            print(self.components[name])
            print("")

    # -----------------------------------------------------------------

    def write(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Writing ...")

        # Write the properties
        self.write_properties()

        # Write the components
        self.write_components()

    # -----------------------------------------------------------------

    def write_properties(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Writing the properties ...")

        # Determine the path and save
        path = self.output_path_file("properties.dat")
        self.properties.saveto(path)

    # -----------------------------------------------------------------

    def write_components(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Writing the components ...")

        # Loop over the components
        for name in self.components:

            # Determine the path
            path = self.output_path_file(name + ".mod")

            # Save the model
            self.components[name].saveto(path)
Exemple #39
0
def get_galaxy_info(name, position):

    """
    This function ...
    :param name:
    :param position:
    :return:
    """

    # Obtain more information about this galaxy
    try:

        ned_result = Ned.query_object(name)
        ned_entry = ned_result[0]

        # Get a more common name for this galaxy (sometimes, the name obtained from NED is one starting with 2MASX .., use the PGC name in this case)
        if ned_entry["Object Name"].startswith("2MASX "): gal_name = name
        else: gal_name = ned_entry["Object Name"]

        # Get the redshift
        gal_redshift = ned_entry["Redshift"]
        if isinstance(gal_redshift, np.ma.core.MaskedConstant): gal_redshift = None

        # Get the type (G=galaxy, HII ...)
        gal_type = ned_entry["Type"]
        if isinstance(gal_type, np.ma.core.MaskedConstant): gal_type = None

    except astroquery.exceptions.RemoteServiceError:

        # Set attributes
        gal_name = name
        gal_redshift = None
        gal_type = None

    except astroquery.exceptions.TimeoutError:

        # Set attributes
        gal_name = name
        gal_redshift = None
        gal_type = None

    except:

        # Set attributes
        gal_name = name
        gal_redshift = None
        gal_type = None

    # Create a new Vizier object and set the row limit to -1 (unlimited)
    viz = Vizier(keywords=["galaxies", "optical"])
    viz.ROW_LIMIT = -1

    # Query Vizier and obtain the resulting table
    result = viz.query_object(name.replace(" ", ""), catalog=["VII/237"])

    # Not found ... TODO: fix this ... this object was in the first query output
    if len(result) == 0: return name, position, None, None, [], None, None, None, None, None, None

    table = result[0]

    # Get the correct entry (sometimes, for example for mergers, querying with the name of one galaxy gives two hits! We have to obtain the right one each time!)
    if len(table) == 0: raise ValueError("The galaxy could not be found under this name")
    elif len(table) == 1: entry = table[0]
    else:

        entry = None

        # Some rows don't have names, if no match is found based on the name just take the row that has other names defined
        rows_with_names = []
        for row in table:
            if row["ANames"]: rows_with_names.append(row)

        # If only one row remains, take that one for the galaxy we are looking for
        if len(rows_with_names) == 1: entry = rows_with_names[0]

        # Else, loop over the rows where names are defined and look for a match
        else:
            for row in rows_with_names:

                names = row["ANames"]

                if name.replace(" ", "") in names or gal_name.replace(" ", "") in names:

                    entry = row
                    break

        # If no matches are found, look for the table entry for which the coordinate matches the given position (if any)
        if entry is None and position is not None:
            for row in table:
                if np.isclose(row["_RAJ2000"], position.ra.value) and np.isclose(row["_DEJ2000"], position.dec.value):
                    entry = row
                    break

    # Note: another temporary fix
    if entry is None: return name, position, None, None, [], None, None, None, None, None, None

    # Get the right ascension and the declination
    position = SkyCoordinate(ra=entry["_RAJ2000"], dec=entry["_DEJ2000"], unit="deg", frame="fk5")

    # Get the names given to this galaxy
    gal_names = entry["ANames"].split() if entry["ANames"] else []

    # Get the size of the galaxy
    ratio = np.power(10.0, entry["logR25"]) if entry["logR25"] else None
    diameter = np.power(10.0, entry["logD25"]) * 0.1 * Unit("arcmin") if entry["logD25"] else None

    #print("  D25_diameter = ", diameter)

    radial_profiles_result = viz.query_object(name, catalog="J/ApJ/658/1006")

    if len(radial_profiles_result) > 0:

        radial_profiles_entry = radial_profiles_result[0][0]

        gal_distance = radial_profiles_entry["Dist"] * Unit("Mpc")
        gal_inclination = Angle(radial_profiles_entry["i"], "deg")
        gal_d25 = radial_profiles_entry["D25"] * Unit("arcmin")

    else:

        gal_distance = None
        gal_inclination = None
        gal_d25 = None

    # Get the size of major and minor axes
    gal_major = diameter
    gal_minor = diameter / ratio if diameter is not None and ratio is not None else None

    # Get the position angle of the galaxy
    gal_pa = Angle(entry["PA"] - 90.0, "deg") if entry["PA"] else None

    # Create and return a new Galaxy instance
    return gal_name, position, gal_redshift, gal_type, gal_names, gal_distance, gal_inclination, gal_d25, gal_major, gal_minor, gal_pa
Exemple #40
0
GAIA_X_match = pd.read_csv(csv_path+csv_gaia_id)
GAIA_X_match = GAIA_X_match[GAIA_X_match['Identifier'].isin(data_csv["Name"].tolist())] # only select those identifiers also in the actual dataframe

# find data of all stars contained within the cross match and the data csv, not containing any NaNs
data_csv = data_csv[data_csv["Name"].isin(GAIA_X_match['Identifier'].tolist())]

# all stars to be queried
starnames = GAIA_X_match["Identifier"].tolist()
GAIA_ids = GAIA_X_match['GaiaID'].values.astype(str)

# look up GAIA catalog inputs for the stars
plxs = []
e_plxs = []
starnames_GAIA = []
for starname,GAIA_id in zip(starnames,GAIA_ids):
    star = Vizier.query_object(starname, catalog=["Gaia"])
    for table_name in star.keys():
        dataframe = star[table_name].to_pandas()
        if re.search(r'345\/gaia2',table_name):
            table_of_sourcenames = star[table_name]["Source"]
            dataframe = star[table_name].to_pandas()
            matchindex = [i for i,j in enumerate(table_of_sourcenames) if re.search(GAIA_id,str(j))]
            PLX = dataframe.iloc[matchindex]["Plx"].values
            e_PLX = dataframe.iloc[matchindex]["e_Plx"].values
    plxs.append(PLX[0])
    e_plxs.append(e_PLX[0])
    print(starname)

# convert lists to numpy arrays
PLXs = np.array(plxs).astype(str)
e_PLXs = np.array(e_plxs).astype(str)
              result_table['FLUX_BIBCODE_z'][0].decode('utf-8'))
    else:
        print("M", args.target, "SDSS z", result_table['FLUX_z'][0], "0.05 # ",
              result_table['FLUX_BIBCODE_z'][0].decode('utf-8'),"; e_mag set to 0.05")         

print("#")
    

#2: get Tycho magnitudes from vizier (Tycho2 catalog and supplements)
    
# need to request all columns since by default, no error columns are returned.    
vquery=Vizier(columns=["**"])

# query catalogs
res = vquery.query_object(args.target,catalog=["I/259/tyc2",
                                               "I/259/suppl_1",
                                               "I/259/suppl_2"])

# some weird but necessary type conversion stuff
for table_name in res.keys(): tyc2table = res[table_name]

# print results
if len(res) > 0: # to make sure star is in the catalog; fails otherwise
    print("#")
    print("# Tycho 2")
    print("#")
    if tyc2table['BTmag'][0] != None:
        print("M", args.target, "Tycho Bt", tyc2table['BTmag'][0],
              tyc2table['e_BTmag'][0]," # ",
              "2000A&A...355L..27H")
    
Exemple #42
0
        diff = tab['{} mag'.format(oneBand)] - tab['{}mag_2mass'.format(
            oneBand)]

        plot_density_scatter(axArr[bandInd, 0], tab['{} mag'.format(oneBand)],
                             diff)
        axArr[bandInd, 0].set_ylim(-0.2, 0.2)
        axArr[bandInd, 0].set_ylabel(r"{} (UKIRT) - {} (2MASS)".format(
            oneBand, oneBand))
        axArr[bandInd, 0].set_xlabel("{} (UKIRT)".format(oneBand))

        plot_density_scatter(axArr[bandInd, 1], tab['J mag'] - tab['K mag'],
                             diff)
        axArr[bandInd, 1].set_xlabel("J (UKIRT) - K (UKIRT)")
        axArr[bandInd, 1].set_ylim(-0.2, 0.2)


#    minmax_show = [np.nanmin(tab['J mag']),np.nanmax(tab['Jmag_2mass'])]
#plt.plot(minmax_show,1.0)
    plt.show()

    return tm_res
"""
Vizier.query_object("NGC 2506",catalog="II/246/out")
result = Vizier.query_object("NGC 2506")

#catalog_list = Vizier.find_catalogs('NGC 2506')
tm_res = Vizier(catalog='II/246/out').query_region("NGC 2506",radius=0.3 *u.deg)[0]
catalog="II/246/out"
"""
class GalaxyDecomposer(DecompositionComponent):
    
    """
    This class...
    """

    def __init__(self, config=None):

        """
        The constructor ...
        :param config:
        :return:
        """

        # Call the constructor of the base class
        super(GalaxyDecomposer, self).__init__(config)

        # The NGC name of the galaxy
        self.ngc_id = None
        self.ngc_id_nospaces = None

        # The Vizier querying object
        self.vizier = Vizier()
        self.vizier.ROW_LIMIT = -1

        # The bulge and disk parameters
        self.parameters = Map()

        # The SKIRT execution context
        self.skirt = SkirtExec()

        # The bulge and disk model
        self.bulge = None
        self.disk = None

        # The bulge and disk image
        self.bulge_image = None
        self.disk_image = None
        self.model_image = None

        # The projection systems
        self.projections = dict()

        # The instruments
        self.instruments = dict()

        # The reference coordinate system
        self.reference_wcs = None

        # The PSF (of the reference image) for convolution with the simulated images
        self.psf = None

    # -----------------------------------------------------------------

    @classmethod
    def from_arguments(cls, arguments):

        """
        This function ...
        :param arguments:
        :return:
        """

        # Create a new GalaxyDecomposer instance
        decomposer = cls(arguments.config)

        # Set the input and output path
        decomposer.config.path = arguments.path

        # Return the new instance
        return decomposer

    # -----------------------------------------------------------------

    def run(self):

        """
        This function ...
        :return:
        """

        # 1. Call the setup function
        self.setup()

        # 2. Get the parameters
        self.get_parameters()

        # 3. Create the models
        self.create_models()

        # 4. Create the projection systems
        self.create_projections()

        # 4. Create the instruments
        self.create_instruments()

        # 2. Simulate the bulge and disk images
        self.simulate()

        # 3. Writing
        self.write()

    # -----------------------------------------------------------------

    def setup(self):

        """
        This function ...
        :return:
        """

        # Call the setup function of the base class
        super(GalaxyDecomposer, self).setup()

        # TEMP: provide a cfg file for this class
        self.config.bulge_packages = 1e7
        self.config.disk_packages = 1e8

        # Get the NGC name of the galaxy
        self.ngc_id = catalogs.get_ngc_name(self.galaxy_name)
        self.ngc_id_nospaces = self.ngc_id.replace(" ", "")

        # Get the coordinate system describing the pixel grid of the prepared images
        reference_path = fs.join(self.prep_path, self.reference_image, "result.fits")
        self.reference_wcs = CoordinateSystem.from_file(reference_path)

        # Load the PSF
        aniano = AnianoKernels()
        self.psf = aniano.get_psf("PACS_160")

    # -----------------------------------------------------------------

    def get_parameters(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Getting the structural galaxy parameters from the S4G catalog ...")

        # Tracing spiral density waves in M81: (Kendall et. al, 2008)
        # - Sersic bulge:
        #    n = 2.62
        #    Re = 46.2 arcsec
        #    b/a = 0.71
        #    PA = −31.9°
        # - Exponential disk:
        #    Rs = 155.4 arcsec
        #    b/a = 0.52
        #    PA = −28.3°

        # Query the S4G catalog using Vizier for general parameters
        self.get_general_parameters()

        # Get the decomposition parameters
        #self.get_p4() currently (writing on 31 of march 2016) there is a problem with the effective radius values
        # (at least for M81) on Vizier as well as in the PDF version of table 7 (S4G models homepage).

        # Parse the S4G table 8 to get the decomposition parameters
        self.get_parameters_from_table()

        #self.parameters.bulge.n = 2.62
        #self.parameters.bulge.PA = Angle(-31.9 + 90., "deg")
        #self.parameters.bulge.q = 0.71
        #value = 46.2 * Unit("arcsec")
        #self.parameters.bulge.Re = (self.parameters.distance * value).to("pc", equivalencies=dimensionless_angles())

        #value = 155.4 * Unit("arcsec")
        #self.parameters.disk.hr = (self.parameters.distance * value).to("pc", equivalencies=dimensionless_angles())
        #self.parameters.disk.q = 0.52
        #self.parameters.disk.PA = Angle(-28.3 + 90., "deg")

    # -----------------------------------------------------------------

    def get_general_parameters(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Querying the S4G catalog ...")

        # Get parameters from S4G catalog
        result = self.vizier.query_object(self.galaxy_name, catalog=["J/PASP/122/1397/s4g"])
        table = result[0]

        # Galaxy name for S4G catalog
        self.parameters.galaxy_name = table["Name"][0]

        # Galaxy center from decomposition (?)
        ra_center = table["_RAJ2000"][0]
        dec_center = table["_DEJ2000"][0]
        center = SkyCoordinate(ra=ra_center, dec=dec_center, unit="deg", frame='fk5')
        self.parameters.center = center

        # Distance
        self.parameters.distance = table["Dmean"][0] * Unit("Mpc")
        self.parameters.distance_error = table["e_Dmean"][0] * Unit("Mpc")

        # Major axis, ellipticity, position angle
        self.parameters.major_arcsec = table["amaj"][0] * Unit("arcsec")
        self.parameters.major = (self.parameters.distance * self.parameters.major_arcsec).to("pc", equivalencies=dimensionless_angles())

        # Ellipticity
        self.parameters.ellipticity = table["ell"][0]
        self.parameters.position_angle = Angle(table["PA"][0] + 90.0, Unit("deg"))

        # Magnitudes
        asymptotic_ab_magnitude_i1 = table["__3.6_"][0]
        asymptotic_ab_magnitude_i2 = table["__4.5_"][0]
        asymptotic_ab_magnitude_i1_error = table["e__3.6_"][0]
        asymptotic_ab_magnitude_i2_error = table["e__4.5_"][0]

        self.parameters.i1_mag = asymptotic_ab_magnitude_i1
        self.parameters.i1_mag_error = asymptotic_ab_magnitude_i1_error
        self.parameters.i2_mag = asymptotic_ab_magnitude_i2
        self.parameters.i2_mag_error = asymptotic_ab_magnitude_i2_error

        self.parameters.i1_fluxdensity = unitconversion.ab_to_jansky(self.parameters.i1_mag) * Unit("Jy")
        i1_fluxdensity_lower = unitconversion.ab_to_jansky(self.parameters.i1_mag + self.parameters.i1_mag_error) * Unit("Jy")
        i1_fluxdensity_upper = unitconversion.ab_to_jansky(self.parameters.i1_mag - self.parameters.i1_mag_error) * Unit("Jy")
        i1_error = ErrorBar(i1_fluxdensity_lower, i1_fluxdensity_upper, at=self.parameters.i1_fluxdensity)
        self.parameters.i1_error = i1_error.average

        self.parameters.i2_fluxdensity = unitconversion.ab_to_jansky(self.parameters.i2_mag) * Unit("Jy")
        i2_fluxdensity_lower = unitconversion.ab_to_jansky(self.parameters.i2_mag + self.parameters.i2_mag_error) * Unit("Jy")
        i2_fluxdensity_upper = unitconversion.ab_to_jansky(self.parameters.i2_mag - self.parameters.i2_mag_error) * Unit("Jy")
        i2_error = ErrorBar(i2_fluxdensity_lower, i2_fluxdensity_upper, at=self.parameters.i2_fluxdensity)
        self.parameters.i2_error = i2_error.average

        # Other ...
        #absolute_magnitude_i1 = table["M3.6"][0]
        #absolute_magnitude_i2 = table["M4.5"][0]
        #stellar_mass = 10.0**table["logM_"][0] * u.Unit("Msun")

        # Inform the user
        log.info("Querying the catalog of radial profiles for 161 face-on spirals ...")

        # Radial profiles for 161 face-on spirals (Munoz-Mateos+, 2007)
        radial_profiles_result = self.vizier.query_object(self.galaxy_name, catalog="J/ApJ/658/1006")

        distance = float(radial_profiles_result[0][0]["Dist"])
        inclination = Angle(float(radial_profiles_result[0][0]["i"]), "deg")

        # Set the inclination
        self.parameters.inclination = inclination

    # -----------------------------------------------------------------

    def get_p4(self):

        """
        This function ...
        :return:
        """

        #http://vizier.cfa.harvard.edu/viz-bin/VizieR?-source=J/ApJS/219/4

        # J/ApJS/219/4: S4G pipeline 4: multi-component decompositions (Salo+, 2015)

        #  - J/ApJS/219/4/galaxies: Parameters of the galaxies; center, outer orientation, sky background; and 1-component Sersic fits (tables 1 and 6) (2352 rows)
        #  - J/ApJS/219/4/table7: *Parameters of final multicomponent decompositions (Note) (4629 rows)

        # Inform the user
        log.info("Querying the S4G pipeline 4 catalog ...")

        # Get the "galaxies" table
        result = self.vizier.query_object(self.galaxy_name, catalog=["J/ApJS/219/4/galaxies"])
        table = result[0]

        # PA: [0.2/180] Outer isophote position angle
        # e_PA: [0/63] Standard deviation in PA
        # Ell:  [0.008/1] Outer isophote ellipticity
        # e_Ell: [0/0.3] Standard deviation in Ell

        pa = Angle(table["PA"][0] - 90., "deg")
        pa_error = Angle(table["e_PA"][0], "deg")

        ellipticity = table["Ell"][0]
        ellipticity_error = table["e_Ell"][0]

        # Get the "table7" table
        result = self.vizier.get_catalogs("J/ApJS/219/4/table7")
        table = result[0]

        # Name: Galaxy name
        # Mod: Type of final decomposition model
        # Nc: [1/4] Number of components in the model (1-4)
        # Q: [3/5] Quality flag, 5=most reliable
        # C: Physical interpretation of the component
        # Fn: The GALFIT function used for the component (sersic, edgedisk, expdisk, ferrer2 or psf)
        # f1: [0.006/1] "sersic" fraction of the total model flux
        # mag1:  [7/19.4] "sersic" total 3.6um AB magnitude
        # q1: [0.1/1] "sersic" axis ratio
        # PA1: [0.08/180] "sersic" position angle [deg]
        # Re: [0.004/430] "sersic" effective radius (Re) [arcsec]
        # n: [0.01/20] "sersic" parameter n
        # f2: [0.02/1] "edgedisk" fraction of the total model flux
        # mu02: [11.8/24.6] "edgedisk" central surface face-on brightness (µ0) [mag/arcsec2]
        # PA2: [-90/90] "edgedisk" PA [deg]
        # hr2: [1/153] "edgedisk" exponential scale length (hr) [arcsec]
        # hz2: [0.003/39] "edgedisk" z-scale hz [arcsec]
        # f3: [0.02/1] "expdisk" fraction of the total model flux
        # mag3: [6.5/18.1] "expdisk" total 3.6um AB magnitude [mag]
        # q3: [0.1/1] "expdisk" axis ratio
        # PA3: [-90/90] "expdisk" position angle [deg]
        # hr3: [0.7/332] "expdisk" exponential scale length (hr) [arcsec]
        # mu03: [16.4/25.3] "expdisk" central surface face-on brightness (µ0) [mag/arcsec2]
        # f4: [0.003/0.6] "ferrer2" fraction of the total model flux
        # mu04: [16/24.8] "ferrer2" central surface sky brightness (µ0) [mag/arcsec2]
        # q4: [0.01/1] "ferrer2" axis ratio
        # PA4: [-90/90] "ferrer2" position angle [deg]
        # Rbar: [3.7/232.5] "ferrer2" outer truncation radius of the bar (Rbar) [arcsec]
        # f5: [0.001/0.4] "psf" fraction of the total model flux
        # mag5: [11.5/21.1] "psf" total 3.6um AB magnitude [mag]

        indices = tables.find_indices(table, self.ngc_id_nospaces, "Name")

        labels = {"sersic": 1, "edgedisk": 2, "expdisk": 3, "ferrer2": 4, "psf": 5}

        #units = {"f": None, "mag": "mag", "q": None, "PA": "deg", }

        # Loop over the indices
        for index in indices:

            model_type = table["Mod"][index]
            number_of_components = table["Nc"][index]
            quality = table["Q"][index]
            interpretation = table["C"][index]
            functionname = table["Fn"][index]

            component_parameters = Map()

            if self.parameters.model_type is not None: assert model_type == self.parameters.model_type
            if self.parameters.number_of_components is not None: assert number_of_components == self.parameters.number_of_components
            if self.parameters.quality is not None: assert quality == self.parameters.quality
            self.parameters.model_type = model_type
            self.parameters.number_of_components = number_of_components
            self.parameters.quality = quality

            for key in table.colnames:

                if not key.endswith(str(labels[functionname])): continue

                parameter = key[:-1]

                value = table[key][index]

                if parameter == "PA":
                    value = Angle(value + 90., "deg")
                    if quadrant(value) == 2: value = value - Angle(180., "deg")
                    elif quadrant(value) == 3: value = value + Angle(180., "deg")

                    if value.to("deg").value > 180.: value = value - Angle(360., "deg")
                    elif value.to("deg").value < -180.: value = value + Angle(360., "deg")
                elif parameter == "mag":
                    parameter = "fluxdensity"
                    value = unitconversion.ab_to_jansky(value) * Unit("Jy")
                elif parameter == "mu0": value = value * Unit("mag/arcsec2")
                elif parameter == "hr":
                    value = value * Unit("arcsec")
                    value = (self.parameters.distance * value).to("pc", equivalencies=dimensionless_angles())
                elif parameter == "hz":
                    value = value * Unit("arcsec")
                    value = (self.parameters.distance * value).to("pc", equivalencies=dimensionless_angles())

                component_parameters[parameter] = value

            if functionname == "sersic":

                re = table["Re"][index] * Unit("arcsec")
                component_parameters["Re"] = (self.parameters.distance * re).to("pc", equivalencies=dimensionless_angles())
                component_parameters["n"] = table["n"][index]

            elif functionname == "ferrer2":

                rbar = table["Rbar"][index] * Unit("arcsec")
                component_parameters["Rbar"] = (self.parameters.distance * rbar).to("pc", equivalencies=dimensionless_angles())

            if interpretation == "B": # bulge

                self.parameters.bulge = component_parameters

            elif interpretation == "D": # disk

                self.parameters.disk = component_parameters

            else: raise RuntimeError("Unrecognized component: " + interpretation)

         # Determine the full path to the parameters file
        path = fs.join(self.components_path, "parameters.dat")

        # Write the parameters to the specified location
        write_parameters(self.parameters, path)

    # -----------------------------------------------------------------

    def get_parameters_from_table(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Parsing S4G table 8 to get the decomposition parameters ...")

        #The table columns are:
        # (1) the running number (1-2352),
        # (2) the galaxy name,
        # (3) the type of final decomposition model,
        # (4) the number N of components in the final model , and
        # (5) the quality flag Q.

        # 6- 8 for unresolved central component ('psf'; phys, frel,  mag),
        # 9-15 for inner sersic-component ('sersic1'; phys,  frel,  mag,    re,    ar,      pa,    n),
        # 16-22 for inner disk-component ('expo1'; phys,  frel,  mag,    hr,    ar,      pa,   mu0),
        # 23-28 for inner ferrers-component ('ferrers1'; phys,  frel,  mu0,   rout,   ar,     pa),
        # 29-34 for inner edge-on disk component ('edgedisk1';  phys, frel, mu0,  rs,    hs,      pa ).
        # 35-41 for outer sersic-component ('sersic2'; phys,  frel,  mag,    re,    ar,      pa,    n),
        # 42-49 for outer disk-component ('expo2'; phys,  frel,  mag,    hr,    ar,      pa,   mu0),
        # 50-55 for outer ferrers-component ('ferrers2'; phys,  frel,  mu0,   rout,   ar,     pa),
        # 56-61 for outer edge-on disk component ('edgedisk2';  phys, frel, mu0,  rs,    hs,      pa ).

        sersic_1_index = 8
        disk_1_index = 15

        #For each function:

        #the first entry stands for the physical intepretation of the component:
        #'N' for a central source (or unresolved bulge), 'B' for a bulge (or elliptical), 'D' for a disk, 'BAR' for a bar, and 'Z' for an edge-on disk.

        # 'rel    =  the relative contribution of the component to the total model flux,
        #  mag    =  the component's total 3.6 micron AB magnitude,
        #  mu0    =  the central  surface brightness (mag/arcsec^2;  de-projected central surface brightness for expdisk and edgedisk, and
        #                                                          sky-plane central surface brightness for ferrer)
        #  ar     =  axial ratio
        #  pa     =  position angle (degrees ccw from North)
        #  n      =  sersic index
        #  hr     =  exponential scale lenght (arcsec)
        #  rs     =  radial scale lenght (arcsec)
        #  hs     =  vertical scale height (arcsec)
        #  rout   =  bar outer truncation radius (arcsec)

        # SERSIC: phys,  frel,  mag,    re,    ar,      pa,    n
        # DISK: phys,  frel,  mag,    hr,    ar,      pa,   mu0

        with open(local_table_path, 'r') as s4g_table:

            for line in s4g_table:

                splitted = line.split()

                if len(splitted) < 2: continue

                name = splitted[1]

                # Only look at the line corresponding to the galaxy
                if name != self.ngc_id_nospaces: continue

                self.parameters.model_type = splitted[2]
                self.parameters.number_of_components = splitted[3]
                self.parameters.quality = splitted[4]

                # BULGE
                self.parameters.bulge = Map()
                #self.parameters.bulge.interpretation = splitted[sersic_1_index].split("|")[1]
                self.parameters.bulge.f = float(splitted[sersic_1_index + 1])
                mag = float(splitted[sersic_1_index + 2])
                self.parameters.bulge.fluxdensity = unitconversion.ab_to_jansky(mag) * Unit("Jy")

                # Effective radius in pc
                re_arcsec = float(splitted[sersic_1_index + 3]) * Unit("arcsec")
                self.parameters.bulge.Re = (self.parameters.distance * re_arcsec).to("pc", equivalencies=dimensionless_angles())

                self.parameters.bulge.q = float(splitted[sersic_1_index + 4])
                self.parameters.bulge.PA = Angle(float(splitted[sersic_1_index + 5]) - 90., "deg")
                self.parameters.bulge.n = float(splitted[sersic_1_index + 6])

                # DISK
                self.parameters.disk = Map()
                #self.parameters.disk.interpretation = splitted[disk_1_index].split("|")[1]
                self.parameters.disk.f = float(splitted[disk_1_index + 1])
                mag = float(splitted[disk_1_index + 2])
                self.parameters.disk.fluxdensity = unitconversion.ab_to_jansky(mag) * Unit("Jy")

                # Scale length in pc
                hr_arcsec = float(splitted[disk_1_index + 3]) * Unit("arcsec")
                self.parameters.disk.hr = (self.parameters.distance * hr_arcsec).to("pc", equivalencies=dimensionless_angles())

                self.parameters.disk.q = float(splitted[disk_1_index + 4]) # axial ratio
                self.parameters.disk.PA = Angle(float(splitted[disk_1_index + 5]) - 90., "deg")
                self.parameters.disk.mu0 = float(splitted[disk_1_index + 6]) * Unit("mag/arcsec2")

    # -----------------------------------------------------------------

    def get_spiral_properties(self):

        """
        This function ...
        :return:
        """

        # http://vizier.cfa.harvard.edu/viz-bin/VizieR?-source=J/A+A/582/A86

        # J/A+A/582/A86: Catalogue of features in the S4G (Herrera-Endoqui+, 2015)

        # - J/A+A/582/A86/table2: Properties of bars, ring- and lens-structures in the S4G (2387 rows)
        # - J/A+A/582/A86/table3: Properties of spiral arms in the S4G (1854 rows)

        # Get table2
        result = self.vizier.query_object(self.galaxy_name, catalog=["J/A+A/582/A86/table2"])
        table = result[0]

        # Name: Galaxy name
        # Class: Morphological classification
        # Type: Type of feature
        # sma: Semi-major axis [arcsec]
        # PA: Position angle [deg]
        # Ell: Ellipticity
        # smaEll: Semi-major axis from ellipticity [arcsec]
        # dsma: Deprojected semi-major axis [arcsec]
        # dPA: Deprojected position angle [deg]
        # dEll: Deprojected ellipticity
        # dsmaEll: Deprojexted semi-major axis from Ell [arcsec]
        # Qual: [1/3] Quality flag

        # Get table 3
        result = self.vizier.query_object(self.galaxy_name, catalog=["J/A+A/582/A86/table3"])
        table = result[0]

        # Name: Galaxy name
        # Class: Morphological classification
        # Type: Type of arms
        # Segment: Segment
        # Pitchang: Pitch angle [deg]
        # ri: Inner radius [arcsec]
        # ro: Outer radius [arcsec]
        # Qual: [1/2] Quality flag

    # -----------------------------------------------------------------

    def create_models(self):

        """
        :return:
        """

        # Create the bulge model
        self.create_bulge_model()

        # Create the disk model
        self.create_disk_model()

    # -----------------------------------------------------------------

    def create_bulge_model(self):

        """
        :return:
        """

        # Create a Sersic model for the bulge
        self.bulge = SersicModel.from_galfit(self.parameters.bulge, self.parameters.inclination, self.parameters.disk.PA)

    # -----------------------------------------------------------------

    def create_disk_model(self):

        """
        :return:
        """

        # Create an exponential disk model for the disk
        self.disk = ExponentialDiskModel.from_galfit(self.parameters.disk, self.parameters.inclination, self.parameters.disk.PA)

    # -----------------------------------------------------------------

    def create_projections(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Creating the projection systems ...")

        # Create the 'earth' projection system
        azimuth = 0.0
        self.projections["earth"] = GalaxyProjection.from_wcs(self.reference_wcs, self.parameters.center, self.parameters.distance,
                                                              self.parameters.inclination, azimuth, self.parameters.disk.PA)

        # Create the face-on projection system
        self.projections["faceon"] = FaceOnProjection.from_wcs(self.reference_wcs, self.parameters.center, self.parameters.distance)

        # Create the edge-on projection system
        self.projections["edgeon"] = EdgeOnProjection.from_wcs(self.reference_wcs, self.parameters.center, self.parameters.distance)

    # -----------------------------------------------------------------

    def create_instruments(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Creating the instruments ...")

        # Loop over the projection systems
        for name in self.projections:

            # Create the instrument from the projection system
            self.instruments[name] = SimpleInstrument.from_projection(self.projections[name])

    # -----------------------------------------------------------------

    def simulate(self):

        """
        This function ...
        :return:
        """

        # Simulate the stellar bulge without deprojection
        self.simulate_bulge_simple()

        # Simulate the stellar bulge
        self.simulate_bulge()

        # Simulate the stellar disk
        self.simulate_disk()

        # Simulate the bulge + disk model
        self.simulate_model()

    # -----------------------------------------------------------------

    def simulate_bulge_simple(self):

        """
        :return:
        """

        # Inform the user
        log.info("Creating ski file to simulate the bulge image ...")

        # Load the bulge ski file template
        bulge_template_path = fs.join(template_path, "bulge.ski")
        ski = SkiFile(bulge_template_path)

        # Set the number of photon packages
        ski.setpackages(self.config.bulge_packages)

        # Change the ski file parameters
        # component_id, index, radius, y_flattening=1, z_flattening=1
        ski.set_stellar_component_sersic_geometry(0, self.parameters.bulge.n, self.parameters.bulge.Re, y_flattening=self.parameters.bulge.q)

        # Remove all existing instruments
        ski.remove_all_instruments()

        # Create the instrument
        distance = self.parameters.distance
        inclination = 0.0
        azimuth = Angle(90., "deg")
        #position_angle = self.parameters.bulge.PA + Angle(90., "deg") # + 90° because we can only do y_flattening and not x_flattening
        position_angle = self.parameters.bulge.PA
        pixels_x = self.reference_wcs.xsize
        pixels_y = self.reference_wcs.ysize
        pixel_center = self.parameters.center.to_pixel(self.reference_wcs)
        center = Position(0.5*pixels_x - pixel_center.x - 0.5, 0.5*pixels_y - pixel_center.y - 0.5)
        center_x = center.x * Unit("pix")
        center_y = center.y * Unit("pix")
        center_x = (center_x * self.reference_wcs.pixelscale.x.to("deg/pix") * distance).to("pc", equivalencies=dimensionless_angles())
        center_y = (center_y * self.reference_wcs.pixelscale.y.to("deg/pix") * distance).to("pc", equivalencies=dimensionless_angles())
        field_x_angular = self.reference_wcs.pixelscale.x.to("deg/pix") * pixels_x * Unit("pix")
        field_y_angular = self.reference_wcs.pixelscale.y.to("deg/pix") * pixels_y * Unit("pix")
        field_x_physical = (field_x_angular * distance).to("pc", equivalencies=dimensionless_angles())
        field_y_physical = (field_y_angular * distance).to("pc", equivalencies=dimensionless_angles())
        fake = SimpleInstrument(distance, inclination, azimuth, position_angle, field_x_physical, field_y_physical, pixels_x, pixels_y, center_x, center_y)

        # Add the instrument
        ski.add_instrument("earth", fake)

        # Create the directory to simulate the bulge
        simple_bulge_directory = fs.join(self.components_path, "bulge_simple")
        fs.create_directory(simple_bulge_directory)

        # Determine the path to the ski file
        ski_path = fs.join(simple_bulge_directory, "bulge.ski")

        # Save the ski file to the new path
        ski.saveto(ski_path)

        # Determine the path to the simulation output directory
        out_path = fs.join(simple_bulge_directory, "out")

        # Create the output directory
        fs.create_directory(out_path)

        # Create a SkirtArguments object
        arguments = SkirtArguments()

        # Adjust the parameters
        arguments.ski_pattern = ski_path
        arguments.output_path = out_path
        arguments.single = True   # we expect a single simulation from the ski pattern

        # Inform the user
        log.info("Running the bulge simulation ...")

        # Run the simulation
        simulation = self.skirt.run(arguments, silent=False if log.is_debug() else True)

        # Determine the path to the output FITS file
        bulge_image_path = fs.join(out_path, "bulge_earth_total.fits")

        # Check if the output contains the "bulge_earth_total.fits" file
        if not fs.is_file(bulge_image_path):
            raise RuntimeError("Something went wrong with the simple bulge simulation: output FITS file missing")

    # -----------------------------------------------------------------

    def simulate_bulge(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Creating ski file to simulate the bulge image ...")

        # Load the bulge ski file template
        bulge_template_path = fs.join(template_path, "bulge.ski")
        ski = SkiFile(bulge_template_path)

        # Set the number of photon packages
        ski.setpackages(self.config.bulge_packages)

        # Set the bulge geometry
        ski.set_stellar_component_geometry(0, self.bulge)

        # Remove all existing instruments
        ski.remove_all_instruments()

        # Add the instruments
        for name in self.instruments: ski.add_instrument(name, self.instruments[name])

        # Determine the path to the ski file
        ski_path = fs.join(self.bulge_path, "bulge.ski")

        # Save the ski file to the new path
        ski.saveto(ski_path)

        # Determine the path to the simulation output directory
        out_path = fs.join(self.bulge_path, "out")

        # Create the output directory
        fs.create_directory(out_path)

        # Create a SkirtArguments object
        arguments = SkirtArguments()

        # Adjust the parameters
        arguments.ski_pattern = ski_path
        arguments.output_path = out_path
        arguments.single = True   # we expect a single simulation from the ski pattern

        # Inform the user
        log.info("Running the bulge simulation ...")

        # Run the simulation
        simulation = self.skirt.run(arguments, silent=False if log.is_debug() else True)

        # Determine the path to the output FITS file
        bulge_image_path = fs.join(out_path, "bulge_earth_total.fits")

        # Check if the output contains the "bulge_earth_total.fits" file
        if not fs.is_file(bulge_image_path):
            raise RuntimeError("Something went wrong with the bulge simulation: output FITS file missing")

        # Open the bulge image
        self.bulge_image = Frame.from_file(bulge_image_path)

        # Set the coordinate system of the bulge image
        self.bulge_image.wcs = self.reference_wcs

        # Debugging
        log.debug("Rescaling the bulge image to the bulge flux density at 3.6 micron ...")

        # Rescale to the 3.6um flux density
        fluxdensity = self.parameters.bulge.fluxdensity
        self.bulge_image *= fluxdensity.to("Jy").value / np.sum(self.bulge_image)
        self.bulge_image.unit = "Jy"

        # Debugging
        log.debug("Convolving the bulge image to the PACS 160 resolution ...")

        # Convolve the frame to the PACS 160 resolution
        self.bulge_image = self.bulge_image.convolved(self.psf)

    # -----------------------------------------------------------------

    def simulate_disk(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Creating ski file to simulate the disk image ...")

        # Load the disk ski file template
        disk_template_path = fs.join(template_path, "disk.ski")
        ski = SkiFile(disk_template_path)

        # Set the number of photon packages
        ski.setpackages(self.config.disk_packages)

        # Change the ski file parameters
        ski.set_stellar_component_geometry(0, self.disk)

        # Remove all existing instruments
        ski.remove_all_instruments()

        # Add the instruments
        for name in self.instruments: ski.add_instrument(name, self.instruments[name])

        # Determine the path to the ski file
        ski_path = fs.join(self.disk_path, "disk.ski")

        # Save the ski file to the new path
        ski.saveto(ski_path)

        # Determine the path to the simulation output directory
        out_path = fs.join(self.disk_path, "out")

        # Create the output directory
        fs.create_directory(out_path)

        # Create a SkirtArguments object
        arguments = SkirtArguments()

        # Adjust the parameters
        arguments.ski_pattern = ski_path
        arguments.output_path = out_path
        arguments.single = True   # we expect a single simulation from the ski pattern

        # Inform the user
        log.info("Running the disk simulation ...")

        # Run the simulation
        simulation = self.skirt.run(arguments, silent=False if log.is_debug() else True)

        # Determine the path to the output FITS file
        disk_image_path = fs.join(out_path, "disk_earth_total.fits")

        # Check if the output contains the "disk_earth_total.fits" file
        if not fs.is_file(disk_image_path):
            raise RuntimeError("Something went wrong with the disk simulation: output FITS file missing")

        # Open the disk image
        self.disk_image = Frame.from_file(disk_image_path)

        # Set the coordinate system of the disk image
        self.disk_image.wcs = self.reference_wcs

        # Debugging
        log.debug("Rescaling the disk image to the disk flux density at 3.6 micron ...")

        # Rescale to the 3.6um flux density
        fluxdensity = self.parameters.disk.fluxdensity
        self.disk_image *= fluxdensity.to("Jy").value / np.sum(self.disk_image)
        self.disk_image.unit = "Jy"

        # Debugging
        log.debug("Convolving the disk image to the PACS 160 resolution ...")

        # Convolve the frame to the PACS 160 resolution
        self.disk_image = self.disk_image.convolved(self.psf)

    # -----------------------------------------------------------------

    def simulate_model(self):

        """
        This function ...
        """

        # Inform the user
        log.info("Creating ski file to simulate the bulge+disk model image ...")

        # Load the disk ski file template
        disk_template_path = fs.join(template_path, "model.ski")
        ski = SkiFile(disk_template_path)

        # Set the number of photon packages
        ski.setpackages(self.config.disk_packages)

        # Change the ski file parameters
        ski.set_stellar_component_geometry(0, self.disk)
        ski.set_stellar_component_geometry(1, self.bulge)

        # Set the luminosities of the two components
        ski.set_stellar_component_luminosities(0, [self.parameters.disk.f])
        ski.set_stellar_component_luminosities(1, [self.parameters.bulge.f])

        # Remove all existing instruments
        ski.remove_all_instruments()

        # Add the instruments
        for name in self.instruments: ski.add_instrument(name, self.instruments[name])

        # Determine the path to the ski file
        ski_path = fs.join(self.model_path, "model.ski")

        # Save the ski file to the new path
        ski.saveto(ski_path)

        # Determine the path to the simulation output directory
        out_path = fs.join(self.model_path, "out")

        # Create the output directory
        fs.create_directory(out_path)

        # Create a SkirtArguments object
        arguments = SkirtArguments()

        # Adjust the parameters
        arguments.ski_pattern = ski_path
        arguments.output_path = out_path
        arguments.single = True   # we expect a single simulation from the ski pattern

        # Inform the user
        log.info("Running the disk+bulge simulation ...")

        # Run the simulation
        simulation = self.skirt.run(arguments, silent=False if log.is_debug() else True)

        # Determine the path to the output FITS file
        model_image_path = fs.join(out_path, "model_earth_total.fits")

        # Check if the output contains the "model_earth_total.fits" file
        if not fs.is_file(model_image_path):
            raise RuntimeError("Something went wrong with the disk+bulge simulation: output FITS file missing")

        # Open the model image
        self.model_image = Frame.from_file(model_image_path)

        # Set the coordinate system of the model image
        self.model_image.wcs = self.reference_wcs

        # Debugging
        log.debug("Rescaling the model image to the bulge+disk flux density at 3.6 micron ...")

        # Rescale to the 3.6um flux density
        fluxdensity = self.parameters.bulge.fluxdensity + self.parameters.disk.fluxdensity # sum of bulge and disk component flux density
        self.model_image *= fluxdensity.to("Jy").value / np.sum(self.model_image)
        self.model_image.unit = "Jy"

        # Debugging
        log.debug("Convolving the model image to the PACS 160 resolution ...")

        # Convolve the frame to the PACS 160 resolution
        self.model_image = self.model_image.convolved(self.psf)

    # -----------------------------------------------------------------

    def write(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Writing ...")

        # Write out the final bulge and disk images
        self.write_images()

        # Write out the parameters in a data file
        self.write_parameters()

        # Write the projection systems
        self.write_projections()

        # Write out the disk ellipse
        self.write_disk_ellipse()

    # -----------------------------------------------------------------

    def write_images(self):

        """
        This function ...
        :return:
        """

        # Determine the path to the bulge image and save it
        final_bulge_path = fs.join(self.components_path, "bulge.fits")
        self.bulge_image.save(final_bulge_path)

        # Determine the path to the disk image and save it
        final_disk_path = fs.join(self.components_path, "disk.fits")
        self.disk_image.save(final_disk_path)

        # Determine the path to the model image and save it
        final_model_path = fs.join(self.components_path, "model.fits")
        self.model_image.save(final_model_path)

    # -----------------------------------------------------------------

    def write_parameters(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Writing data file with parameters ...")

        # Determine the full path to the parameters file
        path = fs.join(self.components_path, "parameters.dat")

        # Write the parameters to the specified location
        write_parameters(self.parameters, path)

    # -----------------------------------------------------------------

    def write_projections(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Writing the projection systems ...")

        # Loop over the projection systems
        for name in self.projections:

            # Determine the path to the projection file
            path = fs.join(self.components_path, name + ".proj")

            # Write the projection system
            self.projections[name].save(path)

    # -----------------------------------------------------------------

    def write_disk_ellipse(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Writing regions file with disk ellipse ...")

        minor = (1.0 - self.parameters.ellipticity) * self.parameters.major_arcsec

        # Ellipse radius
        radius = Extent(self.parameters.major_arcsec, minor)

        # Create sky ellipse
        sky_ellipse = SkyEllipse(self.parameters.center, radius, self.parameters.position_angle)

        # Create region
        region = SkyRegion()
        region.append(sky_ellipse)
        region_path = fs.join(self.components_path, "disk.reg")
        region.save(region_path)