Esempio n. 1
0
def signal_to_noise(fname, col=1, method='var', fname_cov=''):
    """Return signal to noise ratio for WL measurements in file fname.

    Parameters
    ----------
    fname: string
        Input file name
    col: integer
        Column number for signal, default=1
    method: string
        Noise method, 'var' (default), 'cov'
    fname_cov: string
        Covariance file, optional

    Returns
    -------
    SNR: float
        signa to noise ratio
    """

    if method == 'var':
        dat  = ascii.read(fname)
        y    = dat[dat.keys()[col]]  # gt or DeltaSigma
        dy   = dat['sqrt_Dcor']
        snr  = athena.signal_to_noise_w2_var(y, dy)
    else:
        mkstuff.error('Method \'{}\' not (yet) implemented'.format(method))
 
    return snr
Esempio n. 2
0
def plot_histograms(dir, X, hist_type='logM', out_dir='plots', out_name_ext='', verbose=False):
    """Plot cluster mass and redshift histograms"""

    if max(X) < 0:
        # X is array of [-1] if no corresponding column in cluster data found (see get_cluster_data)
        if verbose is True:
            print('Columns for hist_type \'{}\' not valid/not found, skipping plot_histogram'.format(hist_type))
        return

    if hist_type == 'logM':
        # Mass
        x      = np.log10(X)
        Mman, Mexp = mkstuff.frexp10(X.mean())
        text   = 'mean $M = {} \\times 10^{{{}}}$'.format('%.1f' % Mman, Mexp)
        xlabel = '$\\log M$'
        create_histogram(dir, out_dir, x, xlabel, text, hist_type, name_ext=out_name_ext)

    elif hist_type == 'z':
        # Redshift
        x = X
        text = 'mean $z$ = {}'.format('%.2f' % X.mean())
        xlabel = '$z$'
        create_histogram(dir, out_dir, x, xlabel, text, hist_type, name_ext=out_name_ext)

    elif hist_type == 'SNR':
        # Signal-to-noise
        x = X
        text      = 'mean SNR = {}'.format('%.2f' % X.mean())
        xlabel    = 'SNR{}'.format(out_name_ext)
        create_histogram(dir, out_dir, x, xlabel, text, hist_type, name_ext=out_name_ext)

    else:
        mkstuff.error('Wrong hist_type \'{}\''.format(hist_type))
Esempio n. 3
0
def stack(stack, n_cl=-1, fname='wgl_1.txt', subtype='wgl', verbose=False):
    """Stack shear profiles of clusters. Call 'mean_allcolumns.pl'.
       (Formerly in wl_cluster.py.)

    Parameters
    ----------
    stack    : string
        Stack type: 'physical', 'angular'
    n_cl     : integer
        Number of clusters for checking, default=-1. If < 0 no check is performed.
    fname    : string
        File name of wgl output file to be stacked
    subtype  : string
        'wgl' (default), 'wgl_res', 'DeltaSigma'
    verbose  : bool
        Verbose output

    Returns
    -------
    None
     """

    if stack is None:
        if verbose is True:
            print('No stacking')
        return

    # TODO: Only use clusters with non-zero profile, modify -t option.

    in_name = fname

    if n_cl < 0:
        t_flag = ''
    else:
        t_flag = '-t {}'.format(n_cl)

    if subtype == 'wgl' or subtype == 'DeltaSigma':
        w_flags = '-w 3'            # Weight column (w)
    elif subtype == 'wgl_res':
        w_flags = '-w 2 -f 1/x^2'   # rms column, use as w=1/rms^2
    else:
        mkstuff.error('Unknown subtype \'{}\''.format(subtype))

    cmd = 'mean_allcolumns.pl -k {} {} -r {}'.format(t_flag, w_flags, in_name)

    n, out_mgs, err_msg = mkstuff.run_cmd(cmd, verbose=verbose)

    if n == 0:
        mkstuff.error('No  file \'{}\' found for stacking, exiting populationWL.stack with error'.format(in_name))

    mkstuff.mkdir_p('stack')
    os.rename('{}.mean'.format(in_name), 'stack/{}.mean'.format(in_name))
    os.rename('{}.rms'.format(in_name), 'stack/{}.rms'.format(in_name))
    os.rename('{}.meanrms'.format(in_name), 'stack/{}.meanrms'.format(in_name))

    return n
Esempio n. 4
0
    def __init__(self, name, ra, dec, z, M, unit, scale=1):

        self.n   = len(name)
        if self.n != len(ra) or self.n != len(dec) or self.n != len(z):
            mkstuff.error('Unequal lengths for cluster input data')

        self.name = [str(n) for n in name]
        self.ra   = athena.unit_to_rad(ra, unit)
        self.dec  = athena.unit_to_rad(dec, unit)
        self.z    = z
        self.M    = M
        self.unit = unit
        self.scale = scale
Esempio n. 5
0
def get_bg_cat_zmin(dir, lensing_cat_base, lensing_ext, z):
    """Return background (lensing) catalogue with zmin > z.

    Parameters
    ----------
    dir: string
        Directory of catalogues
    lensing_cat_base: string
        Catalogue base name
    lensing_ext: string
        Catalogue extension
    z: float
        Minimum redshift

    Returns
    -------
    cat_lens: string
        Catalogue name. '' if no match found.
    """

    # Find all lensing catalogues
    base    = '{}/{}_zmin_'.format(dir, lensing_cat_base)
    pattern = '{}*.{}'.format(base, lensing_ext)
    files = glob.glob(pattern)
    if len(files) == 0:
        pattern = '{}.{}'.format(dir, lensing_cat_base, lensing_ext)
        print(pattern)
        if os.path.isfile(pattern):
            return pattern
        else:
            mkstuff.error('No matching lensing catalogue slices \'{}\' found'.format(pattern))

    # Extract zmin from file name
    min_zmin = 99
    cat_lens = ''
    for f in files:
        m = re.findall('{}(.*).{}'.format(base, lensing_ext), f)
        if len(m) == 0:
            continue
        this_zmin = float(m[0])
        if this_zmin >= z:
            if min_zmin > this_zmin:
                min_zmin = this_zmin
                cat_lens = f

    return cat_lens
Esempio n. 6
0
def run_athena_list(config_names, suffixes=None, n_cpu=1, version='C', verbose=False, run=True):
    """Run athena.

    Parameters
    ----------
    config_names: list of strings
        list of config names
    suffixes: list of strings
	list of suffix names, default empty, uses config_names instead
    n_cpu: integer
        number of CPUs, default=1
    version: string
        One of ['C' (default)]
    verbose: boolean
        verbose output, default False
    run: boolean
	if False, do not run athena. Default = True
    
    Returns
    -------
    None
    """

    athena_path = get_athena_path(version=version)
    if verbose:
        q_ath = ''
    else:
        q_ath = ' -q'


    cmds = []
    for i, cn in enumerate(config_names):
        if os.path.isfile(cn):
            if suffixes is None or len(suffixes) == 0:
                suff = cn
            else:
                suff = suffixes[i]
            cmds.append('{}/athena -c {} --out_suf {}{}'.format(athena_path, cn, suff, q_ath))

    if verbose is True:
        print('Running {} athena jobs on {} CPUs'.format(len(cmds), n_cpu))

    res = mkstuff.run_cmd_list(cmds, ncpu=n_cpu, verbose=verbose, run=run)
    if res != 0:
        mkstuff.error('At least one athena run returned error code {}'.format(res))
Esempio n. 7
0
def get_unit(field, verbose=False, exit=False):
    """Returns unit for angular scales, from header line with format e.g. 'theta[UNIT]'.
       If exit=True, exists with error. If exit=False, returns unit_default.
    """

    for u in units.keys():
        if u in field:
            if verbose == True:
                print('Unit of input angular scales = {0}'.format(u))
            return u

    if exit == True:
        mkstuff.error('No unit found, exiting')

    if verbose == True:
        warnings.warn('No unit for angular scales found, assuming \'{0}\''.format(unit_default))

    return unit_default
Esempio n. 8
0
def get_athena_path(version='C'):
    """Return path to athena.

    Parameters
    ----------
    version: string
        One of ['C' (default)]

    Returns
    -------
    path: string
        /path/to/athena
    """

    if version == 'C':
        return '{0}/athena_git_Euclid/bin'.format(os.environ['HOME'])
    else:
        mkstuff.error('Wrong athena verions {}'.format(version))
Esempio n. 9
0
def get_col_label(subtype, unit=''):
    """Return column names and plot labels.

    Parameters
    ----------
    subtype: string
        'wgl', 'wgl_res', 'DeltaSigma'
    unit: string
        '', 'M_sun/pc2'

    Returns
    -------
    None
    col: array of strings
        column names
    label: array of strings
        labels
    ylabel: string
        y-axis label
    """

    if unit == '' and subtype == 'DeltaSigma':
        unit = 'M_sun/pc2'

    if unit != '':
        unit_str   = '[{}]'.format(unit)
        unit_label =  '[{}]'.format(u.Unit(unit).to_string('latex_inline'))

    if subtype == 'wgl':
        col    = ['g_t', 'g_x', 'sqrt_Dcor', 'sqrt_Dcor']
        label  = ['$\\gamma_{\\rm t}$', '$\\gamma_\\times$']
        ylabel = 'Tangential and cross-shear'
    elif subtype == 'wgl_res':
        col    = ['g_t_resample', 'g_t_rms_resample']
        label  = ['$\\gamma_{\\rm t}$']
        ylabel = 'Tangential shear'
    elif subtype == 'DeltaSigma':
        col    = ['Delta_Sigma_t{}'.format(unit_str), 'Delta_Sigma_x{}'.format(unit_str), 'sqrt_Dcor', 'sqrt_Dcor']
        label  = ['$\\Delta \\Sigma_{{\\rm t}}$ {}'.format(unit_label), '$\\Delta \\Sigma_\\times$ {}'.format(unit_label)]
        ylabel = 'Projected overdensity'
    else:
        mkstuff.error('Unknown subtype \'{}\''.format(subtype))

    return col, label, ylabel
Esempio n. 10
0
    def dtheta_theta(self, i):
        """Returns dtheta_i * theta_i
        """

        if self.binning == 'lin':
            # dtheta * theta
            d = self.delta * self.theta[i]
        elif self.binning == 'log':
            # d log theta * theta^2 = d theta / theta * theta^2 = dtheta * theta
            d = self.delta * self.theta[i] * self.theta[i]
        elif self.binning == 'ireg':
            if i != self.length-1:
                d = (self.theta[i+1] - self.theta[i]) * self.theta[i]
            else:
                d = (self.theta[i] - self.theta[i-1]) * self.theta[i]
        else:
            mkstuff.error('Invalid binning type \'{}\''.format(self.binning))

        return d
Esempio n. 11
0
def read_xi(fname, file_format, force_reg, verbose=False):
    """Reads a shear-shear correlation function file (ascii or fits).
    """

    my_file_format = get_file_format(file_format, fname)

    if verbose:
        print("File format is {0}".format(my_file_format))

    if my_file_format == 'fits':
        theta, xip, xim, xix = read_xi_fits(fname, force_reg, verbose)
    elif my_file_format == 'ascii':
        theta, xip, xim, xix = read_xi_ascii(fname, force_reg, verbose)
    else:
       mkstuff.error('read_xi: Wrong file format \'{0}\', has to be \'ascii\' or \'fits\' (option \'-f\')'.format(my_file_format))

    xi = xi_data(theta, xip, xim, xix, my_file_format, force_reg=force_reg, verbose=verbose)

    return xi
Esempio n. 12
0
def get_theta_xlabel(scale_mode, wgl, unit_force=None):
    """Return angular scale column and xlabel string, from file wgl 

    Parameters
    ----------
    scale_mode: string
        'physical' or 'angular'
    wgl: Class table
        wgl file content, e.g. obtained from ascii.read
    unit_force: string, default=None
        If not None, use unit_force for label

    Returns
    -------
    theta: array of double
        content of scale column
    xlabel: string
        x-axis label
    """

    if scale_mode == "angular":
        x        = ['theta']
        for key in units:
            x.append('theta[{}]'.format(key))
    elif scale_mode == "physical":
        x        = ['R[Mpc]', 'R']

    theta = None
    for myx in x:
        if myx in wgl.keys():
            theta  = wgl[myx]
            xlabel = myx
            break

    if unit_force is not None:
        pattern = re.compile('\[.*\]')
        xlabel = pattern.sub('[{}]'.format(unit_force), xlabel)

    if theta is None:
        mkstuff.error('Column for angular scale not found')

    return theta, xlabel
Esempio n. 13
0
def get_unit_fits(header, verbose=False, exit=False):

    if 'UNITS' in header.keys():
        unit = header['UNITS']
    else:
        if exit == True:
            mkstuff.error('No unit found, exiting')
        if verbose == True:
            mkstuff.warning('No \'UNITS\' keyword in fits header found, assuming default unit for angular scales \'{0}\''.format(unit_default))
        return unit_default

    # Go through valid units and compare to assigned one (from header)
    for u in units.keys():
        if u == unit:
            if verbose == True:
                print('Unit of input angular scales = {0}'.format(u))
            return u

    if verbose == True:
        mkstuff.warning('Unit from fits header (\'UNITS\' = \'{0}\') not a valid unit, reverting to default unit \'{1}\''.format(unit, unit_default))
        return unit_default
Esempio n. 14
0
def check_fields(header, strings, indices, exit, verbose=False):
    """Prints error (warning) message if header (array of strings) does not contain necessary (optional)
       strings.
    """

    if header is None:
        if verbose is True:
            mkstuff.warning('No header found, using default columns')
        if exit is True:
            return False

    for i in range(len(strings)):
        if len(header) <= indices[i] or not strings[i] in header[indices[i]]:
            msg = 'Field \'{0}\' not found in header (expected in field #{1})'.format(strings[i], indices[i])
            if exit is True:
                mkstuff.error(msg)
            else:
                if verbose is True:
                    mkstuff.warning('{}, continuing'.format(msg))
                return False

    return True
Esempio n. 15
0
def read_xi_ascii(fname, force_reg, verbose=False):
    """Read a shear-shear correlation function ascii file.
    """

    if verbose is True: print('Reading input xi file \'{0}\''.format(fname))

    xi, ncomment, header  = mkstuff.read_table(fname, True)

    if xi.shape[1] <= 2:
        mkstuff.error('Input file \'{0}\' has only {1} columns. Minimum columns are (theta, xi+, xi-)'.format(fname, xi.shape[1]))

    if ncomment > 0:
        fields = header[0].replace('#', '').split()
    else:
        fields = None

    # Required fields
    check_fields(fields, in_cols.xi_names, in_cols.xi_indices, True, verbose)

    theta = xi[:, in_cols.scale.num]
    xip   = xi[:, in_cols.signal1['xi'].num]
    xim   = xi[:, in_cols.signal2['xi'].num]

    # Optional fields
    has_xix = check_fields(fields, in_cols.xi_names_opt, in_cols.xi_indices_opt, False, verbose)
    if has_xix == True:
        xix  = xi[:, in_cols.parity['xi'].num]
    else:
        xix  = None

    # Units of angular scales
    if fields is not None:
        unit = get_unit(fields[in_cols.scale.num], verbose)
    else:
        unit = unit_default
    theta  = unit_to_rad(theta, unit)

    return theta, xip, xim, xix
Esempio n. 16
0
    def __init__(self, fname=None, zdata=None, zbin=None, err_if_empty=False):

        if fname is None or fname == '':

            if zdata is None and zbin is None:

                if err_if_empty == True:
                    mkstuff.error('No redshift info given, not creating empty nofz')

                # No nofz input given, create empty nofz
                self.z    = None
                self.n    = None
                self.nz   = None
                self.type = 'none'
            elif zdata is None or zbin is None:
                mkstuff.error('Only one of zbin and zdata defined')
            else:
                # Input histogram
                self.n, self.z = np.histogram(zdata, zbin)

                # Right-most input bin nofz.z[-1] is correctly interpreted as last bin edge.
                # Returned n has one entry less than z.
                self.n = np.append(self.n, [0])
                self.nz   = self.z.shape[0] - 1
                self.type = 'hist'

        else:

            if zdata is not None or zbin is not None:
                mkstuff.error('Both n(z) input file name and zdata/zbin defined')
            else:
                # Read nofz from file
                dat, n, head = mkstuff.read_table(fname, count_comment=True)
                if re.search('hist', head[0]) is not None:
                    self.z    = dat[:,0]
                    self.n    = dat[:,1]
                    self.nz   = dat.shape[0] - 1
                    self.type = 'hist'
                else:
                    mkstuff.error('nofz: only \'hist\' type implemented')

        self.is_valid()
Esempio n. 17
0
    def is_valid(self):
        """Checks whether nofz has valid format and content.
         """

        if self.n[-1] != 0:
            mkstuff.error('Wrong histogram format, frequency for rightmost bin corner has ' +
                              'to be 0, found instead {}'.format(self.n[-1]))
        if sum(self.n) == 0:
            mkstuff.error('sum of n(z) is zero')
        if max(self.z) == 0:
            mkstuff.error('All redshifts are zero')

        return True
Esempio n. 18
0
def read_pkappa_band_fits(fname, verbose=False, stop=False):
    """Read from file the band-power spectrum P_i.

    Parameters
    ----------
    fname: string
        file name
    verbose: boolean
        verbose mode if True, default False
    stop: boolean
        If True (default), exit with error. If False, return None

    Returns
    -------
    pb: class pkappa_data
        data
    """

    try:
        hdulist = fits.open(fname)
    except IOError:
        msg = 'File \'{}\' does not exist'.format(fname)
        mkstuff.error(msg, stop=stop, verbose=verbose)
        if stop is False:
            return None

    for ihdu, hdu in enumerate(hdulist):

        header_col_vals = get_header_columns_from_fits_table(hdu)
        if header_col_vals is not None:
            check_fields(header_col_vals, in_cols.Pb_names, in_cols.Pb_indices, True, verbose)

            if verbose is True:
                print('Table with band-power spectrum data found at hdu #{}'.format(ihdu))

            ell  = hdu.data[in_cols.scale_F.name]
            P_E  = hdu.data[in_cols.signal1['Pb'].name]
            P_B  = hdu.data[in_cols.signal2['Pb'].name]
            P_EB = hdu.data[in_cols.parity['Pb'].name]
            ell_lower = hdu.data[in_cols.scale_lo['Pb'].name]
            ell_upper = hdu.data[in_cols.scale_hi['Pb'].name]

            has_errors = check_fields(header_col_vals, in_cols.Pb_names_err, in_cols.Pb_indices_err, False, verbose)
            if has_errors is True:
                dP_E  = hdu.data[in_cols.error1['Pb'].name]
                dP_B  = hdu.data[in_cols.error2['Pb'].name]
                dP_EB = hdu.data[in_cols.error_parity['Pb'].name]
            else:
                dP_E  = None
                dP_B  = None
                dP_EB = None

            break

    hdulist.close()

    if ell is None:
        pb = None
    else:
        pb = pkappa_data(ell[0], ell[1], band=True, error=True, Nell=len(ell))
        pb.ell  = ell
        pb.pE   = P_E
        pb.pB   = P_B
        pb.pEB  = P_EB
        pb.dpE  = dP_E
        pb.dpB  = dP_B
        pb.dpEB = dP_EB

    return pb
Esempio n. 19
0
def read_xi_rms_resample_fits(fname, force_reg, verbose=False, stop=True):
    """Read a shear-shear correlation function resample (containing mean and rms) file in fits format.

    Parameters
    ----------
    fname: string
        input file name
    force_reg: boolean
        if True, forces regular binning
    verbose: boolean
        Verbose output, default=False
    stop: boolean
        If True (default), exit with error. If False, return None

    Returns
    -------
    theta: array of double, length N_theta
        angular scales
    xi: array of double, length N_resample x N_theta
        resampled correlation values
    resample_type: string
        resample type, 'bootstrap', 'jackknife'
    """

    hdulist = fits.open(fname)
    nhdu    = len(hdulist)
    theta   = None
    for hdu in range(nhdu):
        hdu_type = type(hdulist[hdu])

        # TODO: Use get_header_columns_from_fits_table
        if hdu_type == fits.hdu.table.BinTableHDU or hdu_type == fits.hdu.table.TableHDU:

            if 'OBJECT' in hdulist[hdu].header and hdulist[hdu].header.get('OBJECT') == 'xi.resample':
                if verbose == True:
                    print('Table found in fits file {0} (hdu #{1})'.format(input, hdu))
                header_all_keys = hdulist[hdu].header.keys()
                header_col_keys = [s for s in header_all_keys if 'TTYPE' in s]
                header_col_vals = [hdulist[hdu].header.get(s) for s in header_col_keys]

                # Required fields
                check_fields(header_col_vals, in_cols.xi_res_names, in_cols.xi_res_indices, True, verbose)
                theta     = hdulist[hdu].data[in_cols.scale.name]
                xi_p_res  = hdulist[hdu].data[in_cols.signal1['xi_res'].name]
                xi_m_res  = hdulist[hdu].data[in_cols.signal2['xi_res'].name]
                rms_p_res = hdulist[hdu].data[in_cols.rms1['xi_res'].name]
                rms_m_res = hdulist[hdu].data[in_cols.rms2['xi_res'].name]

                # Units of angular scales
                unit  = get_unit_fits(hdulist[hdu].header, verbose)
                theta = unit_to_rad(theta, unit)

                break


    hdulist.close()

    if theta is None:
        msg = 'No hdu with resample information (OBJECT \'xi.resample\') found in file {}'.format(fname)
        if stop is True:
            mkstuff.error(msg)
        else:
            if verbose is True:
                print('{}, continuing'.format(msg))
            return None

    return theta, xi_p_res, xi_m_res, rms_p_res, rms_m_res
Esempio n. 20
0
def read_xi_resample_fits(fname, force_reg, check_OBJECT=None, verbose=False):
    """Read a shear-shear correlation function file with individual resample results, in fits format (xi_p or xi_m).

    Parameters
    ----------
    fname: string
        input file name
    force_reg: boolean
        if True, forces regular binning
    check_OBJECT: string
        If not None, checks whether the given string equals the value of the OBJECT key in
        fits header, and returns with error if not. Default: None
    verbose: boolean
        Verbose output, default=False

    Returns
    -------
    theta: array of double, length N_theta
        angular scales
    xi: array of double, length N_resample x N_theta
        resampled correlation values
    resample_type: string
        resample type, 'bootstrap', 'jackknife'
    """

    hdulist = fits.open(fname)
    nhdu    = len(hdulist)
    for hdu in range(nhdu):
        hdu_type = type(hdulist[hdu])
    
        # TODO: Use get_header_columns_from_fits_table
        if hdu_type == fits.hdu.table.BinTableHDU or hdu_type == fits.hdu.table.TableHDU:
            if verbose == True:
                print('Table found in fits file {0} (hdu #{1})'.format(input, hdu))
            header_all_keys = hdulist[hdu].header.keys()
            header_col_keys = [s for s in header_all_keys if 'TTYPE' in s]
            header_col_vals = [hdulist[hdu].header.get(s) for s in header_col_keys]

            if check_OBJECT is not None:
                if 'OBJECT' in hdulist[hdu].header:
                    val = hdulist[hdu].header.get('OBJECT')
                    if val != check_OBJECT:
                        mkstuff.error('Found in fits header \'OBJECT = {}\', not equal to required \'\''.format(val, check_OBJECT))
                else:
                        mkstuff.error('Key \'OBJECT\' not found in header')

            if 'TYPE' in hdulist[hdu].header:
                resample_type = hdulist[hdu].header.get('TYPE')
                if resample_type != 'bootstrap' and resample_type != 'jackknife':
                    mkstuff.error('Unknown resample type \'{}\' found in fits file (keyword \'TYPE\')'.format(resample_type))

            check_fields(header_col_vals, [in_cols.xi_names[0]], [in_cols.xi_indices[0]], True, verbose)

            # Angular scales
            theta  = hdulist[hdu].data[in_cols.scale.name]
            unit  = get_unit_fits(hdulist[hdu].header, verbose)
            theta = unit_to_rad(theta, unit)

            # Correlation data
            xi = []
            for i in range(1, len(header_col_vals)):
                key = header_col_vals[i]
                xi_this_res = hdulist[hdu].data[key]
                xi.append(xi_this_res)

            break

    return theta, xi, resample_type
Esempio n. 21
0
def plot_corr_w2_col(theta, w2, xlabel, ylabel, label, title='', SNR=False, scales_SNR=None, with0=False, ylog=False):
    """Plot columns theta versus w2, containing data and error column(s). Called by plot_corr_w2_internal.

    Parameters
    ----------
    theta: array of length N of float
        x-axis data
    w2: array NxNcol of float
        y-axis data, sets of data and error columns
    xlabel: string
        x-axis label
    ylabel: string
        y-axis label
    label: array of length 2 of string
        labels for the two data sets (w2)
    title: string
        title string (default: empty)
    SNR: boolean
        If True, calculates and adds as text to plot signa-to-noise ratio.
    scales_SNR: string, two numbers with separator '_' or ' '
        Minimum and maximum scale for SNR calculation. If None (default) use full range.
    with0: bool
        If True, adds the y=0 line. Default: False
    ylog: bool
        If True, plot log of y-axis, default: False

    Returns
    -------
    None
    """

    color = ['b', 'g']
    msize = [5, 4]
    sign  = [-1, 1]
    f_err, f_lim, log_x = prepare_plot(theta, title, xlabel, ylabel)

    if len(w2) == 4:
        yerr = [w2[2], w2[3]]
        Ndata = 2
    elif len(w2) == 2:
        yerr = [w2[1]]
        Ndata = 1
    else:
        mkstuff.error('Wrong number {} of columns in w2'.format(len(w2)))

    if ylog is True:

        for i in range(0, Ndata):

            # dlogy = dy/y
            yerr[i] = yerr[i] / w2[i]

            id_pos = np.where(w2[i]>0)
            id_neg = np.where(w2[i]<=0)
            y_pos  = np.log10(w2[i][id_pos])
            y_neg  = np.log10(-w2[i][id_neg])
            if i == 0:
                mkplot.make_log_ticks('y', min(y_pos)-2, max(y_pos)+1)

            plt.plot(log_x[id_pos] + sign[i]*f_err, y_pos, '{}.'.format(color[i]), marker='o', markersize=msize[i], label=label[i])
            plt.plot(log_x[id_neg] + sign[i]*f_err, y_neg, '{}o'.format(color[i]), marker='o', mfc='none', markersize=msize[i], label='')
            plt.errorbar(log_x[id_pos] + sign[i]*f_err, y_pos, yerr=yerr[i][id_pos], fmt='none',
                ecolor='{}'.format(color[i]), elinewidth=1, label='')
            plt.errorbar(log_x[id_neg] + sign[i]*f_err, y_neg, yerr=yerr[i][id_neg], fmt='none',
                ecolor='{}'.format(color[i]), elinewidth=0.5, label='')

    else:

        for i in [0, Ndata-1]:
            plt.plot(log_x + sign[i]*f_err, w2[i], '{}'.format(color[i]), linewidth=1, label=label[i])
            plt.errorbar(log_x + sign[i]*f_err, w2[i], yerr=yerr[i], fmt='none', ecolor='{}'.format(color[i]), label='')

        if with0 is True:
            plt.plot([min(log_x), max(log_x)], [0, 0], 'k', linewidth=1, label=None)

    plt.legend(loc='upper right', numpoints=1, frameon=False)

    if SNR is True:

        imin, imax = get_index_range(theta, scales_SNR)

        ax = plt.subplot(1, 1, 1)
        if len(w2) == 4:
            snr   = signal_to_noise_w2_var(w2[0][imin:imax+1], w2[2][imin:imax+1])
            snr_x = signal_to_noise_w2_var(w2[1][imin:imax+1], w2[3][imin:imax+1])
            plt.text(0.95, 0.7, 'SNR_x = {}'.format('%.2f' % snr_x), ha='right', transform=ax.transAxes)
        elif len(w2) == 2:
            snr   = signal_to_noise_w2_var(w2[0][imin:imax+1], w2[1][imin:imax+1])
        plt.text(0.95, 0.75, 'SNR_t = {}'.format('%.2f' % snr), ha='right', transform=ax.transAxes)
        ymin, ymax = plt.ylim()
        dx2 = (log_x[1] - log_x[0])/2
        plt.plot([log_x[imin]-dx2, log_x[imin]-dx2], [ymin, ymax], linestyle='dashed', color='black')
        plt.plot([log_x[imax]+dx2, log_x[imax]+dx2], [ymin, ymax], linestyle='dashed', color='black')

    # From plot_wgl.py:plot_stacked_wgl
    mkplot.update_lim('x', max_new = np.log10(max(theta) * f_lim))