Example #1
0
def run(soltab, axesInPlot, axisInTable='', axisInCol='', axisDiff='', NColFig=0, figSize=[0,0], markerSize=2, minmax=[0,0], log='', \
               plotFlag=False, doUnwrap=False, refAnt='', soltabsToAdd='', makeAntPlot=False, makeMovie=False, prefix='', ncpu=0):
    """
    This operation for LoSoTo implements basic plotting
    WEIGHT: flag-only compliant, no need for weight

    Parameters
    ----------
    axesInPlot : array of str
        1- or 2-element array which says the coordinates to plot (2 for 3D plots).

    axisInTable : str, optional
        the axis to plot on a page - e.g. ant to get all antenna's on one file. By default ''.

    axisInCol : str, optional
        The axis to plot in different colours - e.g. pol to get correlations with different colors. By default ''.

    axisDiff : str, optional
        This must be a len=2 axis and the plot will have the differential value - e.g. 'pol' to plot XX-YY. By default ''.

    NColFig : int, optional
        Number of columns in a multi-table image. By default is automatically chosen.

    figSize : array of int, optional
        Size of the image [x,y], if one of the values is 0, then it is automatically chosen. By default automatic set.

    markerSize : int, optional
        Size of the markers in the 2D plot. By default 2.

    minmax : array of float, optional
        Min max value for the independent variable (0 means automatic). By default 0.

    log : bool, optional
        Use Log='XYZ' to set which axes to put in Log. By default ''.

    plotFlag : bool, optional
        Whether to plot also flags as red points in 2D plots. By default False.

    doUnwrap : bool, optional
        Unwrap phases. By default False.

    refAnt : str, optional
        Reference antenna for phases. By default None.

    soltabsToAdd : str, optional
        Tables to "add" (e.g. 'sol000/tec000'), it works only for tec and clock to be added to phases. By default None.

    makeAntPlot : bool, optional
        Make a plot containing antenna coordinates in x,y and in color the value to plot, axesInPlot must be [ant]. By default False.

    makeMovie : bool, optional
        Make a movie summing up all the produced plots, by default False.

    prefix : str, optional
        Prefix to add before the self-generated filename, by default None.

    ncpu : int, optional
        Number of cpus, by default all available.
    """
    import os, random
    import numpy as np
    from losoto.lib_unwrap import unwrap, unwrap_2d

    logging.info("Plotting soltab: " + soltab.name)

    # input check

    # str2list
    if axisInTable == '': axisInTable = []
    else: axisInTable = [axisInTable]
    if axisInCol == '': axisInCol = []
    else: axisInCol = [axisInCol]
    if axisDiff == '': axisDiff = []
    else: axisDiff = [axisDiff]

    if len(set(axisInTable + axesInPlot + axisInCol +
               axisDiff)) != len(axisInTable + axesInPlot + axisInCol +
                                 axisDiff):
        logging.error('Axis defined multiple times.')
        return 1

    # just because we use lists, check that they are 1-d
    if len(axisInTable) > 1 or len(axisInCol) > 1 or len(axisDiff) > 1:
        logging.error(
            'Too many TableAxis/ColAxis/DiffAxis, they must be at most one each.'
        )
        return 1

    for axis in axesInPlot + axisInCol + axisDiff:
        if axis not in soltab.getAxesNames():
            logging.error('Axis \"' + axis + '\" not found.')
            return 1

    if makeMovie:
        prefix = prefix + '__tmp__'

    if os.path.dirname(prefix) != '' and not os.path.exists(
            os.path.dirname(prefix)):
        logging.debug('Creating ' + os.path.dirname(prefix) + '.')
        os.makedirs(os.path.dirname(prefix))

    if refAnt == '': refAnt = None
    elif refAnt != 'closest' and not refAnt in soltab.getAxisValues(
            'ant', ignoreSelection=True):
        logging.error('Reference antenna ' + refAnt + ' not found. Using: ' +
                      soltab.getAxisValues('ant')[1])
        refAnt = soltab.getAxisValues('ant')[1]

    minZ, maxZ = minmax

    solset = soltab.getSolset()
    soltabsToAdd = [
        solset.getSoltab(soltabName) for soltabName in soltabsToAdd
    ]

    cmesh = False
    if len(axesInPlot) == 2:
        cmesh = True
        # not color possible in 3D
        axisInCol = []
    elif len(axesInPlot) != 1:
        logging.error('Axes must be a len 1 or 2 array.')
        return 1
    # end input check

    # all axes that are not iterated by anything else
    axesInFile = soltab.getAxesNames()
    for axis in axisInTable + axesInPlot + axisInCol + axisDiff:
        axesInFile.remove(axis)

    # set subplots scheme
    if axisInTable != []:
        Nplots = soltab.getAxisLen(axisInTable[0])
    else:
        Nplots = 1

    # prepare antennas coord in makeAntPlot case
    if makeAntPlot:
        if axesInPlot != ['ant']:
            logging.error(
                'If makeAntPlot is selected the "Axes" values must be "ant"')
            return 1
        antCoords = [[], []]
        for ant in soltab.getAxisValues(
                'ant'):  # select only user-selected antenna in proper order
            antCoords[0].append(+1 * soltab.getSolset().getAnt()[ant][1])
            antCoords[1].append(-1 * soltab.getSolset().getAnt()[ant][0])

    else:
        antCoords = []

    datatype = soltab.getType()

    # start processes for multi-thread
    mpm = multiprocManager(ncpu, _plot)

    # compute dataCube size
    shape = []
    if axisInTable != []: shape.append(soltab.getAxisLen(axisInTable[0]))
    else: shape.append(1)
    if axisInCol != []: shape.append(soltab.getAxisLen(axisInCol[0]))
    else: shape.append(1)
    if cmesh:
        shape.append(soltab.getAxisLen(axesInPlot[1]))
        shape.append(soltab.getAxisLen(axesInPlot[0]))
    else:
        shape.append(soltab.getAxisLen(axesInPlot[0]))

    # will contain the data to pass to each thread to make 1 image
    dataCube = np.ma.zeros(shape=shape, fill_value=np.nan)

    # cycle on files
    if makeMovie: pngs = []  # store png filenames
    for vals, coord, selection in soltab.getValuesIter(
            returnAxes=axisDiff + axisInTable + axisInCol + axesInPlot):

        # set filename
        filename = ''
        for axis in axesInFile:
            filename += axis + str(coord[axis]) + '_'
        filename = filename[:-1]  # remove last _
        if prefix + filename == '': filename = 'plot'

        # axis vals (they are always the same, regulat arrays)
        xvals = coord[axesInPlot[0]]
        # if plotting antenna - convert to number
        if axesInPlot[0] == 'ant':
            xvals = np.arange(len(xvals))

        # if plotting time - convert in h/min/s
        xlabelunit = ''
        if axesInPlot[0] == 'time':
            if xvals[-1] - xvals[0] > 3600:
                xvals = (xvals - xvals[0]) / 3600.  # hrs
                xlabelunit = ' [hr]'
            elif xvals[-1] - xvals[0] > 60:
                xvals = (xvals - xvals[0]) / 60.  # mins
                xlabelunit = ' [min]'
            else:
                xvals = (xvals - xvals[0])  # sec
                xlabelunit = ' [s]'
        # if plotting freq convert in MHz
        elif axesInPlot[0] == 'freq':
            xvals = xvals / 1.e6  # MHz
            xlabelunit = ' [MHz]'

        if cmesh:
            # axis vals (they are always the same, regular arrays)
            yvals = coord[axesInPlot[1]]
            # same as above but for y-axis
            if axesInPlot[1] == 'ant':
                yvals = np.arange(len(yvals))

            if len(xvals) <= 1 or len(yvals) <= 1:
                logging.error(
                    '3D plot must have more then one value per axes.')
                mpm.wait()
                return 1

            ylabelunit = ''
            if axesInPlot[1] == 'time':
                if yvals[-1] - yvals[0] > 3600:
                    yvals = (yvals - yvals[0]) / 3600.  # hrs
                    ylabelunit = ' [hr]'
                elif yvals[-1] - yvals[0] > 60:
                    yvals = (yvals - yvals[0]) / 60.  # mins
                    ylabelunit = ' [min]'
                else:
                    yvals = (yvals - yvals[0])  # sec
                    ylabelunit = ' [s]'
            elif axesInPlot[1] == 'freq':  # Mhz
                yvals = yvals / 1.e6
                ylabelunit = ' [MHz]'
        else:
            yvals = None
            if datatype == 'clock':
                datatype = 'Clock'
                ylabelunit = ' (s)'
            elif datatype == 'tec':
                datatype = 'dTEC'
                ylabelunit = ' (TECU)'
            elif datatype == 'rotationmeasure':
                datatype = 'dRM'
                ylabelunit = r' (rad m$^{-2}$)'
            elif datatype == 'tec3rd':
                datatype = r'dTEC$_3$'
                ylabelunit = r' (rad m$^{-3}$)'
            else:
                ylabelunit = ''

        # cycle on tables
        soltab1Selection = soltab.selection  # save global selection and subselect only axex to iterate
        soltab.selection = selection
        titles = []

        for Ntab, (vals, coord, selection) in enumerate(
                soltab.getValuesIter(returnAxes=axisDiff + axisInCol +
                                     axesInPlot)):

            # set tile
            titles.append('')
            for axis in coord:
                if axis in axesInFile + axesInPlot + axisInCol: continue
                titles[Ntab] += axis + ':' + str(coord[axis]) + ' '
            titles[Ntab] = titles[Ntab][:-1]  # remove last ' '

            # cycle on colors
            soltab2Selection = soltab.selection
            soltab.selection = selection
            for Ncol, (vals, weight, coord, selection) in enumerate(
                    soltab.getValuesIter(returnAxes=axisDiff + axesInPlot,
                                         weight=True,
                                         reference=refAnt)):

                # differential plot
                if axisDiff != []:
                    # find ordered list of axis
                    names = [
                        axis for axis in soltab.getAxesNames()
                        if axis in axisDiff + axesInPlot
                    ]
                    if axisDiff[0] not in names:
                        logging.error("Axis to differentiate (%s) not found." %
                                      axisDiff[0])
                        mpm.wait()
                        return 1
                    if len(coord[axisDiff[0]]) != 2:
                        logging.error(
                            "Axis to differentiate (%s) has too many values, only 2 is allowed."
                            % axisDiff[0])
                        mpm.wait()
                        return 1

                    # find position of interesting axis
                    diff_idx = names.index(axisDiff[0])
                    # roll to first place
                    vals = np.rollaxis(vals, diff_idx, 0)
                    vals = vals[0] - vals[1]
                    weight = np.rollaxis(weight, diff_idx, 0)
                    weight[0][weight[1] == 0] = 0
                    weight = weight[0]
                    del coord[axisDiff[0]]

                # add tables if required (e.g. phase/tec)
                for soltabToAdd in soltabsToAdd:
                    logging.warning('soltabsToAdd not implemented. Ignoring.')
#                    newCoord = {}
#                    for axisName in coord.keys():
#                        # prepare selected on present axes
#                        if axisName in soltabToAdd.getAxesNames():
#                            if type(coord[axisName]) is np.ndarray:
#                                newCoord[axisName] = coord[axisName]
#                            else:
#                                newCoord[axisName] = [coord[axisName]] # avoid being interpreted as regexp, faster
#
#                    soltabToAdd.setSelection(**newCoord)
#                    valsAdd = np.squeeze(soltabToAdd.getValues(retAxesVals=False, weight=False, reference=refAnt))
#
#                    # add missing axes
#                    print ('shape:', vals.shape)
#                    for axisName in coord.keys():
#                        if not axisName in soltabToAdd.getAxesNames():
#                            # find axis positions
#                            axisPos = soltab.getAxesNames().index(axisName)
#                            # create a new axes for the table to add and duplicate the values
#                            valsAdd = np.expand_dims(valsAdd, axisPos)
#                            print ('shape to add:', valsAdd.shape)
#
#                    if soltabToAdd.getType() == 'clock':
#                        valsAdd = 2. * np.pi * valsAdd * coord['freq']
#                    elif soltabToAdd.getType() == 'tec':
#                        valsAdd = -8.44797245e9 * valsAdd / coord['freq']
#                    else:
#                        logging.warning('Only Clock or TEC can be added to solutions. Ignoring: '+soltabToAdd.getType()+'.')
#                        continue
#
#                    if valsAdd.shape != vals.shape:
#                        logging.error('Cannot combine the table '+soltabToAdd.getType()+' with '+soltab.getType()+'. Wrong shape.')
#                        mpm.wait()
#                        return 1
#
#                    vals += valsAdd

# normalize
                if (soltab.getType() == 'phase'
                        or soltab.getType() == 'scalarphase'):
                    vals = normalize_phase(vals)
                if (soltab.getType() == 'rotation'):
                    vals = np.mod(vals + np.pi / 2., np.pi) - np.pi / 2.

                # is user requested axis in an order that is different from h5parm, we need to transpose
                if cmesh:
                    if soltab.getAxesNames().index(
                            axesInPlot[0]) < soltab.getAxesNames().index(
                                axesInPlot[1]):
                        vals = vals.T
                        weight = weight.T

                # unwrap if required
                if (soltab.getType() == 'phase'
                        or soltab.getType() == 'scalarphase') and doUnwrap:
                    if len(axesInPlot) == 1:
                        vals = unwrap(vals)
                    else:
                        flags = np.array((weight == 0), dtype=bool)
                        if not (flags == True).all():
                            vals = unwrap_2d(vals, flags, coord[axesInPlot[0]],
                                             coord[axesInPlot[1]])

                dataCube[Ntab, Ncol] = vals
                sel1 = np.where(weight == 0.)
                sel2 = np.where(np.isnan(vals))
                if cmesh:
                    dataCube[Ntab, Ncol, sel1[0], sel1[1]] = np.ma.masked
                    dataCube[Ntab, Ncol, sel2[0], sel2[1]] = np.ma.masked
                else:
                    dataCube[Ntab, Ncol, sel1[0]] = np.ma.masked
                    dataCube[Ntab, Ncol, sel2[0]] = np.ma.masked

            soltab.selection = soltab2Selection
            ### end cycle on colors

        # if dataCube too large (> 500 MB) do not go parallel
        if np.array(dataCube).nbytes > 1024 * 1024 * 500:
            logging.debug('Big plot, parallel not possible.')
            _plot(Nplots, NColFig, figSize, markerSize, cmesh, axesInPlot,
                  axisInTable, xvals, yvals, xlabelunit, ylabelunit, datatype,
                  prefix + filename, titles, log, dataCube, minZ, maxZ,
                  plotFlag, makeMovie, antCoords, None)
        else:
            mpm.put([
                Nplots, NColFig, figSize, markerSize, cmesh, axesInPlot,
                axisInTable, xvals, yvals, xlabelunit, ylabelunit, datatype,
                prefix + filename, titles, log,
                np.ma.copy(dataCube), minZ, maxZ, plotFlag, makeMovie,
                antCoords
            ])
        if makeMovie: pngs.append(prefix + filename + '.png')

        soltab.selection = soltab1Selection
        ### end cycle on tables
    mpm.wait()

    if makeMovie:

        def long_substr(strings):
            """
            Find longest common substring
            """
            substr = ''
            if len(strings) > 1 and len(strings[0]) > 0:
                for i in range(len(strings[0])):
                    for j in range(len(strings[0]) - i + 1):
                        if j > len(substr) and all(strings[0][i:i + j] in x
                                                   for x in strings):
                            substr = strings[0][i:i + j]
            return substr

        movieName = long_substr(pngs)
        assert movieName != ''  # need a common prefix, use prefix keyword in case
        logging.info('Making movie: ' + movieName)
        # make every movie last 20 sec, min one second per slide
        fps = np.ceil(len(pngs) / 200.)
        ss="mencoder -ovc lavc -lavcopts vcodec=mpeg4:vpass=1:vbitrate=6160000:mbd=2:keyint=132:v4mv:vqmin=3:lumi_mask=0.07:dark_mask=0.2:"+\
                "mpeg_quant:scplx_mask=0.1:tcplx_mask=0.1:naq -mf type=png:fps="+str(fps)+" -nosound -o "+movieName.replace('__tmp__','')+".mpg mf://"+movieName+"*  > mencoder.log 2>&1"
        os.system(ss)
        #for png in pngs: os.system('rm '+png)

    return 0
Example #2
0
def run( soltab, doUnwrap=False, refAnt='', plotName='', ndiv=1 ):
    """
    Find the structure function from phase solutions of core stations.

    Parameters
    ----------
    doUnwrap : bool, optional

    refAnt : str, optional
        Reference antenna, by default the first.

    plotName : str, optional
        Plot file name, by default no plot.

    ndiv : int, optional
        

    """
    import numpy as np
    from losoto.lib_unwrap import unwrap, unwrap_2d

    logging.info("Find structure function for soltab: "+soltab.name)

    # input check
    solType = soltab.getType()
    if solType != 'phase':
       logging.warning("Soltab type of "+soltab._v_name+" is of type "+solType+", should be phase.")
       return 1

    ants = soltab.getAxisValues('ant')
    if refAnt != '' and refAnt != 'closest' and not refAnt in soltab.getAxisValues('ant', ignoreSelection = True):
        logging.error('Reference antenna '+refAnt+' not found. Using: '+ants[1])
        refAnt = ants[1]
    if refAnt == '' and doUnwrap:
        logging.error('Unwrap requires reference antenna. Using: '+ants[1])
        refAnt = ants[1]
    if refAnt == '': refAnt = None

    soltab.setSelection(ant='CS*', update=True)

    posAll = soltab.getSolset().getAnt()

    for vals, weights, coord, selection in soltab.getValuesIter(returnAxes=['freq','pol','ant','time'], weight=True, reference=refAnt):

        # reorder axes
        vals = reorderAxes( vals, soltab.getAxesNames(), ['pol','ant','freq','time'] )
        weights = reorderAxes( weights, soltab.getAxesNames(), ['pol','ant','freq','time'] )

        # order positions
        pos = np.array([list(posAll[ant]) for ant in coord['ant']])

        # avg pols
        vals = np.cos(vals) + 1.j * np.sin(vals)
        vals = np.nansum(vals, axis=0)
        vals = np.angle(vals)
        flags = np.array((weights[0]==0)|(weights[1]==0), dtype=bool)

        # unwrap
        if doUnwrap:
            # remove mean to facilitate unwrapping
            for a, ant in enumerate(coord['ant']):
                if not (flags[a,:,:] == True).all() and ant != refAnt:
                    logging.debug('Unwrapping: '+ant)
                    mean = np.angle( np.nanmean( np.exp(1j*vals[a].flatten()) ))
                    vals[a] -= mean
                    vals[a] = np.mod(vals[a]+np.pi, 2*np.pi) - np.pi
                    vals[a,:,:] = unwrap_2d(vals[a,:,:], flags[a,:,:], coord['freq'], coord['time'])
        
        logging.debug('Computing differential values...')
        t1 = np.ma.array( vals, mask=flags ) # mask flagged data
        dph = t1[np.newaxis]-t1[:,np.newaxis] # ant x ant x freq x time
        D = pos[np.newaxis]-pos[:,np.newaxis] # ant x ant x 3
        D2 = np.triu(np.sqrt(np.sum(D**2,axis=-1))) # calc distance and keep only uppoer triangle larger than 0
        myselect = (D2>0)

        if not doUnwrap:
            logging.debug('Re-normalising...')
            dph = np.mod(dph+np.pi, 2*np.pi) - np.pi
            avgdph = np.ma.average(dph, axis=2) # avg in freq (can do because is between -pi and pi)
            #one extra step to remove most(all) phase wraps, phase wraps disturbe the averaging...
            dph = np.remainder(dph - np.ma.average(avgdph,axis=-1)[:,:,np.newaxis,np.newaxis]+np.pi,2*np.pi) + np.ma.average(avgdph,axis=-1)[:,:,np.newaxis,np.newaxis]-np.pi #center around the avg value
        
        logging.debug('Computing sructure function...')
        avgdph = np.ma.average(dph,axis=2) # avg in freq to reduce noise

        variances = []; pars = []
        avgdph = avgdph[...,avgdph.shape[-1]%ndiv:] # remove a few timeslots to make the array divisible by np.split
        for i, avgdphSplit in enumerate( np.split(avgdph, ndiv, axis=-1) ):
            variance = np.ma.var(avgdphSplit, axis=-1)*(np.average(coord['freq'])/150.e6)**2 # get time variance and rescale to 150 MHz

            # linear regression
            #A = np.ones((2,D2[myselect].shape[0]),dtype=float)
            #A[1,:] = np.log10(D2[myselect][~variance.mask])
            #par = np.dot(np.linalg.inv(np.dot(A,A.T)),np.dot(A,np.log10(variance[myselect])))
            mask = variance[myselect].mask
            A = np.vstack([np.log10(D2[myselect][~mask]), np.ones(len(D2[myselect][~mask]))])
            par = np.linalg.lstsq( A.T, np.log10(variance[myselect][~mask]) )[0] 
            S0 = 10**(-1*par[1]/par[0])
            logging.info(r't%i: beta=%.2f - R_diff=%.2f km' % (i, par[0], S0/1.e3))
            variances.append(variance)
            pars.append(par)

        if plotName != '':
            if plotName.split('.')[-1] != 'png': plotName += '.png' # add png

            if not 'matplotlib' in sys.modules:
                import matplotlib as mpl
                mpl.use("Agg")
            import matplotlib.pyplot as plt
    
            fig = plt.figure()
            fig.subplots_adjust(wspace=0)
            ax = fig.add_subplot(111)
            ax1 = ax.twinx()
            
            for i, variance in enumerate(variances):
                if len(variances) > 1:
                    color = plt.cm.jet(i/float(len(variances)-1)) # from 0 to 1
                else: color = 'black'
                ax.plot(D2[myselect]/1.e3,variance[myselect],marker='o',linestyle='', color=color, markeredgecolor='none', label='T')

                # regression
                par = pars[i]
                x = D2[myselect]
                S0 = 10**(-1*par[1]/par[0])
                if color == 'black': color = 'red' # in case of single color, use red line that is more visible
                ax1.plot(x.flatten()/1.e3, par[0]*np.log10(x.flatten()) + par[1], linestyle='-', color=color, label=r'$\beta=%.2f$ - $R_{\rm diff}=%.2f$ km' % (par[0], S0/1.e3))

            ax.set_xlabel('Distance (km)')
            ax.set_ylabel(r'Phase variance @150 MHz (rad$^2$)')
            ax.set_xscale('log')
            ax.set_yscale('log')

            ymin = np.min(variance[myselect])
            ymax = np.max(variance[myselect])
            ax.set_xlim(xmin=0.1,xmax=3)
            ax.set_ylim(ymin,ymax)
            ax1.set_ylim(np.log10(ymin),np.log10(ymax))
            ax1.legend(loc='lower right', frameon=False)
            ax1.set_yticks([])
        
            logging.warning('Save pic: %s' % plotName)
            plt.savefig(plotName, bbox_inches='tight')

    return 0
Example #3
0
def run(soltab, doUnwrap=False, refAnt='', plotName='', ndiv=1):
    """
    Find the structure function from phase solutions of core stations.

    Parameters
    ----------
    doUnwrap : bool, optional

    refAnt : str, optional
        Reference antenna, by default the first.

    plotName : str, optional
        Plot file name, by default no plot.

    ndiv : int, optional
        

    """
    import numpy as np
    from losoto.lib_unwrap import unwrap, unwrap_2d

    logging.info("Find structure function for soltab: " + soltab.name)

    # input check
    solType = soltab.getType()
    if solType != 'phase':
        logging.warning("Soltab type of " + soltab._v_name + " is of type " +
                        solType + ", should be phase.")
        return 1

    ants = soltab.getAxisValues('ant')
    if refAnt != '' and refAnt != 'closest' and not refAnt in soltab.getAxisValues(
            'ant', ignoreSelection=True):
        logging.error('Reference antenna ' + refAnt + ' not found. Using: ' +
                      ants[1])
        refAnt = ants[1]
    if refAnt == '' and doUnwrap:
        logging.error('Unwrap requires reference antenna. Using: ' + ants[1])
        refAnt = ants[1]
    if refAnt == '': refAnt = None

    soltab.setSelection(ant='CS*', update=True)

    posAll = soltab.getSolset().getAnt()

    for vals, weights, coord, selection in soltab.getValuesIter(
            returnAxes=['freq', 'pol', 'ant', 'time'],
            weight=True,
            reference=refAnt):

        # reorder axes
        vals = reorderAxes(vals, soltab.getAxesNames(),
                           ['pol', 'ant', 'freq', 'time'])
        weights = reorderAxes(weights, soltab.getAxesNames(),
                              ['pol', 'ant', 'freq', 'time'])

        # order positions
        pos = np.array([list(posAll[ant]) for ant in coord['ant']])

        # avg pols
        vals = np.cos(vals) + 1.j * np.sin(vals)
        vals = np.nansum(vals, axis=0)
        vals = np.angle(vals)
        flags = np.array((weights[0] == 0) | (weights[1] == 0), dtype=bool)

        # unwrap
        if doUnwrap:
            # remove mean to facilitate unwrapping
            for a, ant in enumerate(coord['ant']):
                if not (flags[a, :, :] == True).all() and ant != refAnt:
                    logging.debug('Unwrapping: ' + ant)
                    mean = np.angle(np.nanmean(np.exp(1j * vals[a].flatten())))
                    vals[a] -= mean
                    vals[a] = np.mod(vals[a] + np.pi, 2 * np.pi) - np.pi
                    vals[a, :, :] = unwrap_2d(vals[a, :, :], flags[a, :, :],
                                              coord['freq'], coord['time'])

        logging.debug('Computing differential values...')
        t1 = np.ma.array(vals, mask=flags)  # mask flagged data
        dph = t1[np.newaxis] - t1[:, np.newaxis]  # ant x ant x freq x time
        D = pos[np.newaxis] - pos[:, np.newaxis]  # ant x ant x 3
        D2 = np.triu(
            np.sqrt(np.sum(D**2, axis=-1))
        )  # calc distance and keep only uppoer triangle larger than 0
        myselect = (D2 > 0)

        if not doUnwrap:
            logging.debug('Re-normalising...')
            dph = np.mod(dph + np.pi, 2 * np.pi) - np.pi
            avgdph = np.ma.average(
                dph,
                axis=2)  # avg in freq (can do because is between -pi and pi)
            #one extra step to remove most(all) phase wraps, phase wraps disturbe the averaging...
            dph = np.remainder(
                dph -
                np.ma.average(avgdph, axis=-1)[:, :, np.newaxis, np.newaxis] +
                np.pi, 2 * np.pi) + np.ma.average(
                    avgdph,
                    axis=-1)[:, :, np.newaxis,
                             np.newaxis] - np.pi  #center around the avg value

        logging.debug('Computing sructure function...')
        avgdph = np.ma.average(dph, axis=2)  # avg in freq to reduce noise

        variances = []
        pars = []
        avgdph = avgdph[
            ..., avgdph.shape[-1] %
            ndiv:]  # remove a few timeslots to make the array divisible by np.split
        for i, avgdphSplit in enumerate(np.split(avgdph, ndiv, axis=-1)):
            variance = np.ma.var(avgdphSplit, axis=-1) * (
                np.average(coord['freq']) /
                150.e6)**2  # get time variance and rescale to 150 MHz

            # linear regression
            #A = np.ones((2,D2[myselect].shape[0]),dtype=float)
            #A[1,:] = np.log10(D2[myselect][~variance.mask])
            #par = np.dot(np.linalg.inv(np.dot(A,A.T)),np.dot(A,np.log10(variance[myselect])))
            mask = variance[myselect].mask
            A = np.vstack([
                np.log10(D2[myselect][~mask]),
                np.ones(len(D2[myselect][~mask]))
            ])
            par = np.linalg.lstsq(A.T, np.log10(variance[myselect][~mask]))[0]
            S0 = 10**(-1 * par[1] / par[0])
            logging.info(r't%i: beta=%.2f - R_diff=%.2f km' %
                         (i, par[0], S0 / 1.e3))
            variances.append(variance)
            pars.append(par)

        if plotName != '':
            if plotName.split('.')[-1] != 'png': plotName += '.png'  # add png

            if not 'matplotlib' in sys.modules:
                import matplotlib as mpl
                mpl.use("Agg")
            import matplotlib.pyplot as plt

            fig = plt.figure()
            fig.subplots_adjust(wspace=0)
            ax = fig.add_subplot(111)
            ax1 = ax.twinx()

            for i, variance in enumerate(variances):
                if len(variances) > 1:
                    color = plt.cm.jet(
                        i / float(len(variances) - 1))  # from 0 to 1
                else:
                    color = 'black'
                ax.plot(D2[myselect] / 1.e3,
                        variance[myselect],
                        marker='o',
                        linestyle='',
                        color=color,
                        markeredgecolor='none',
                        label='T')

                # regression
                par = pars[i]
                x = D2[myselect]
                S0 = 10**(-1 * par[1] / par[0])
                if color == 'black':
                    color = 'red'  # in case of single color, use red line that is more visible
                ax1.plot(x.flatten() / 1.e3,
                         par[0] * np.log10(x.flatten()) + par[1],
                         linestyle='-',
                         color=color,
                         label=r'$\beta=%.2f$ - $R_{\rm diff}=%.2f$ km' %
                         (par[0], S0 / 1.e3))

            ax.set_xlabel('Distance (km)')
            ax.set_ylabel(r'Phase variance @150 MHz (rad$^2$)')
            ax.set_xscale('log')
            ax.set_yscale('log')

            ymin = np.min(variance[myselect])
            ymax = np.max(variance[myselect])
            ax.set_xlim(xmin=0.1, xmax=3)
            ax.set_ylim(ymin, ymax)
            ax1.set_ylim(np.log10(ymin), np.log10(ymax))
            ax1.legend(loc='lower right', frameon=False)
            ax1.set_yticks([])

            logging.warning('Save pic: %s' % plotName)
            plt.savefig(plotName, bbox_inches='tight')

    return 0
Example #4
0
def run(soltab, axesInPlot, axisInTable='', axisInCol='', axisDiff='', NColFig=0, figSize=[0,0], markerSize=2, minmax=[0,0], log='', \
               plotFlag=False, doUnwrap=False, refAnt='', soltabsToAdd='', makeAntPlot=False, makeMovie=False, prefix='', ncpu=0):
    """
    This operation for LoSoTo implements basic plotting
    WEIGHT: flag-only compliant, no need for weight

    Parameters
    ----------
    axesInPlot : array of str
        1- or 2-element array which says the coordinates to plot (2 for 3D plots).

    axisInTable : str, optional
        the axis to plot on a page - e.g. ant to get all antenna's on one file. By default ''.

    axisInCol : str, optional
        The axis to plot in different colours - e.g. pol to get correlations with different colors. By default ''.

    axisDiff : str, optional
        This must be a len=2 axis and the plot will have the differential value - e.g. 'pol' to plot XX-YY. By default ''.

    NColFig : int, optional
        Number of columns in a multi-table image. By default is automatically chosen.

    figSize : array of int, optional
        Size of the image [x,y], if one of the values is 0, then it is automatically chosen. By default automatic set.

    markerSize : int, optional
        Size of the markers in the 2D plot. By default 2.

    minmax : array of float, optional
        Min max value for the independent variable (0 means automatic). By default 0.

    log : bool, optional
        Use Log='XYZ' to set which axes to put in Log. By default ''.

    plotFlag : bool, optional
        Whether to plot also flags as red points in 2D plots. By default False.

    doUnwrap : bool, optional
        Unwrap phases. By default False.

    refAnt : str, optional
        Reference antenna for phases. By default None.

    soltabsToAdd : str, optional
        Tables to "add" (e.g. 'sol000/tec000'), it works only for tec and clock to be added to phases. By default None.

    makeAntPlot : bool, optional
        Make a plot containing antenna coordinates in x,y and in color the value to plot, axesInPlot must be [ant]. By default False.

    makeMovie : bool, optional
        Make a movie summing up all the produced plots, by default False.

    prefix : str, optional
        Prefix to add before the self-generated filename, by default None.

    ncpu : int, optional
        Number of cpus, by default all available.
    """
    import os, random
    import numpy as np
    from losoto.lib_unwrap import unwrap, unwrap_2d

    logging.info("Plotting soltab: "+soltab.name)

    # input check

    # str2list
    if axisInTable == '': axisInTable = []
    else: axisInTable = [axisInTable]
    if axisInCol == '': axisInCol = []
    else: axisInCol = [axisInCol]
    if axisDiff == '': axisDiff = []
    else: axisDiff = [axisDiff]

    if len(set(axisInTable+axesInPlot+axisInCol+axisDiff)) != len(axisInTable+axesInPlot+axisInCol+axisDiff):
        logging.error('Axis defined multiple times.')
        return 1

    # just because we use lists, check that they are 1-d
    if len(axisInTable) > 1 or len(axisInCol) > 1 or len(axisDiff) > 1:
        logging.error('Too many TableAxis/ColAxis/DiffAxis, they must be at most one each.')
        return 1

    for axis in axesInPlot+axisInCol+axisDiff:
        if axis not in soltab.getAxesNames():
            logging.error('Axis \"'+axis+'\" not found.')
            return 1

    if makeMovie:
        prefix = prefix+'__tmp__'

    if os.path.dirname(prefix) != '' and not os.path.exists(os.path.dirname(prefix)):
        logging.debug('Creating '+os.path.dirname(prefix)+'.')
        os.makedirs(os.path.dirname(prefix))

    if refAnt == '': refAnt = None
    elif refAnt != 'closest' and not refAnt in soltab.getAxisValues('ant', ignoreSelection = True):
        logging.error('Reference antenna '+refAnt+' not found. Using: '+soltab.getAxisValues('ant')[1])
        refAnt = soltab.getAxisValues('ant')[1]

    minZ, maxZ = minmax

    solset = soltab.getSolset()
    soltabsToAdd = [ solset.getSoltab(soltabName) for soltabName in soltabsToAdd ]

    cmesh = False
    if len(axesInPlot) == 2:
        cmesh = True
        # not color possible in 3D
        axisInCol = []
    elif len(axesInPlot) != 1:
        logging.error('Axes must be a len 1 or 2 array.')
        return 1
    # end input check

    # all axes that are not iterated by anything else
    axesInFile = soltab.getAxesNames()
    for axis in axisInTable+axesInPlot+axisInCol+axisDiff:
        axesInFile.remove(axis)

    # set subplots scheme
    if axisInTable != []:
        Nplots = soltab.getAxisLen(axisInTable[0])
    else:
        Nplots = 1

    # prepare antennas coord in makeAntPlot case
    if makeAntPlot:
        if axesInPlot != ['ant']:
            logging.error('If makeAntPlot is selected the "Axes" values must be "ant"')
            return 1
        antCoords = [[],[]]
        for ant in soltab.getAxisValues('ant'): # select only user-selected antenna in proper order
            antCoords[0].append(+1*soltab.getSolset().getAnt()[ant][1])
            antCoords[1].append(-1*soltab.getSolset().getAnt()[ant][0])

    else:
        antCoords = []

    datatype = soltab.getType()

    # start processes for multi-thread
    mpm = multiprocManager(ncpu, _plot)

    # compute dataCube size
    shape = []
    if axisInTable != []: shape.append(soltab.getAxisLen(axisInTable[0]))
    else: shape.append(1)
    if axisInCol != []: shape.append(soltab.getAxisLen(axisInCol[0]))
    else: shape.append(1)
    if cmesh:
        shape.append(soltab.getAxisLen(axesInPlot[1]))
        shape.append(soltab.getAxisLen(axesInPlot[0]))
    else:
        shape.append(soltab.getAxisLen(axesInPlot[0]))
    
    # will contain the data to pass to each thread to make 1 image
    dataCube = np.ma.zeros( shape=shape, fill_value=np.nan )

    # cycle on files
    if makeMovie: pngs = [] # store png filenames
    for vals, coord, selection in soltab.getValuesIter(returnAxes=axisDiff+axisInTable+axisInCol+axesInPlot):

        # set filename
        filename = ''
        for axis in axesInFile:
            filename += axis+str(coord[axis])+'_'
        filename = filename[:-1] # remove last _
        if prefix+filename == '': filename = 'plot'

        # axis vals (they are always the same, regulat arrays)
        xvals = coord[axesInPlot[0]]
        # if plotting antenna - convert to number
        if axesInPlot[0] == 'ant':
            xvals = np.arange(len(xvals))

        # if plotting time - convert in h/min/s
        xlabelunit=''
        if axesInPlot[0] == 'time':
            if xvals[-1] - xvals[0] > 3600:
                xvals = (xvals-xvals[0])/3600.  # hrs
                xlabelunit = ' [hr]'
            elif xvals[-1] - xvals[0] > 60:
                xvals = (xvals-xvals[0])/60.   # mins
                xlabelunit = ' [min]'
            else:
                xvals = (xvals-xvals[0])  # sec
                xlabelunit = ' [s]'
        # if plotting freq convert in MHz
        elif axesInPlot[0] == 'freq':
            xvals = xvals/1.e6 # MHz
            xlabelunit = ' [MHz]'

        if cmesh:
            # axis vals (they are always the same, regular arrays)
            yvals = coord[axesInPlot[1]]
            # same as above but for y-axis
            if axesInPlot[1] == 'ant':
                yvals = np.arange(len(yvals))

            if len(xvals) <= 1 or len(yvals) <=1:
                logging.error('3D plot must have more then one value per axes.')
                mpm.wait()
                return 1

            ylabelunit=''
            if axesInPlot[1] == 'time':
                if yvals[-1] - yvals[0] > 3600:
                    yvals = (yvals-yvals[0])/3600.  # hrs
                    ylabelunit = ' [hr]'
                elif yvals[-1] - yvals[0] > 60:
                    yvals = (yvals-yvals[0])/60.   # mins
                    ylabelunit = ' [min]'
                else:
                    yvals = (yvals-yvals[0])  # sec
                    ylabelunit = ' [s]'
            elif axesInPlot[1] == 'freq':  # Mhz
                yvals = yvals/1.e6
                ylabelunit = ' [MHz]'
        else:
            yvals = None
            if datatype == 'clock':
                datatype = 'Clock'
                ylabelunit = ' (s)'
            elif datatype == 'tec':
                datatype = 'dTEC'
                ylabelunit = ' (TECU)'
            elif datatype == 'rotationmeasure':
                datatype = 'dRM'
                ylabelunit = r' (rad m$^{-2}$)'
            elif datatype == 'tec3rd':
                datatype = r'dTEC$_3$'
                ylabelunit = r' (rad m$^{-3}$)'
            else:
                ylabelunit = ''

        # cycle on tables
        soltab1Selection = soltab.selection # save global selection and subselect only axex to iterate
        soltab.selection = selection
        titles = []

        for Ntab, (vals, coord, selection) in enumerate(soltab.getValuesIter(returnAxes=axisDiff+axisInCol+axesInPlot)):

            # set tile
            titles.append('')
            for axis in coord:
                if axis in axesInFile+axesInPlot+axisInCol: continue
                titles[Ntab] += axis+':'+str(coord[axis])+' '
            titles[Ntab] = titles[Ntab][:-1] # remove last ' '

            # cycle on colors
            soltab2Selection = soltab.selection
            soltab.selection = selection
            for Ncol, (vals, weight, coord, selection) in enumerate(soltab.getValuesIter(returnAxes=axisDiff+axesInPlot, weight=True, reference=refAnt)):

                # differential plot
                if axisDiff != []:
                    # find ordered list of axis
                    names = [axis for axis in soltab.getAxesNames() if axis in axisDiff+axesInPlot]
                    if axisDiff[0] not in names:
                        logging.error("Axis to differentiate (%s) not found." % axisDiff[0])
                        mpm.wait()
                        return 1
                    if len(coord[axisDiff[0]]) != 2:
                        logging.error("Axis to differentiate (%s) has too many values, only 2 is allowed." % axisDiff[0])
                        mpm.wait()
                        return 1

                    # find position of interesting axis
                    diff_idx = names.index(axisDiff[0])
                    # roll to first place
                    vals = np.rollaxis(vals,diff_idx,0)
                    vals = vals[0] - vals[1]
                    weight = np.rollaxis(weight,diff_idx,0)
                    weight[0][ weight[1]==0 ] = 0
                    weight = weight[0]
                    del coord[axisDiff[0]]

                # add tables if required (e.g. phase/tec)
                for soltabToAdd in soltabsToAdd:
                    logging.warning('soltabsToAdd not implemented. Ignoring.')
#                    newCoord = {}
#                    for axisName in coord.keys():
#                        # prepare selected on present axes
#                        if axisName in soltabToAdd.getAxesNames():
#                            if type(coord[axisName]) is np.ndarray:
#                                newCoord[axisName] = coord[axisName]
#                            else:
#                                newCoord[axisName] = [coord[axisName]] # avoid being interpreted as regexp, faster
#
#                    soltabToAdd.setSelection(**newCoord)
#                    valsAdd = np.squeeze(soltabToAdd.getValues(retAxesVals=False, weight=False, reference=refAnt))
#
#                    # add missing axes
#                    print ('shape:', vals.shape)
#                    for axisName in coord.keys():
#                        if not axisName in soltabToAdd.getAxesNames():
#                            # find axis positions
#                            axisPos = soltab.getAxesNames().index(axisName)
#                            # create a new axes for the table to add and duplicate the values
#                            valsAdd = np.expand_dims(valsAdd, axisPos)
#                            print ('shape to add:', valsAdd.shape)
#
#                    if soltabToAdd.getType() == 'clock':
#                        valsAdd = 2. * np.pi * valsAdd * coord['freq']
#                    elif soltabToAdd.getType() == 'tec':
#                        valsAdd = -8.44797245e9 * valsAdd / coord['freq']
#                    else:
#                        logging.warning('Only Clock or TEC can be added to solutions. Ignoring: '+soltabToAdd.getType()+'.')
#                        continue
#
#                    if valsAdd.shape != vals.shape:
#                        logging.error('Cannot combine the table '+soltabToAdd.getType()+' with '+soltab.getType()+'. Wrong shape.')
#                        mpm.wait()
#                        return 1
#
#                    vals += valsAdd

                # normalize
                if (soltab.getType() == 'phase' or soltab.getType() == 'scalarphase'):
                    vals = normalize_phase(vals)
                if (soltab.getType() == 'rotation'):
                    vals = np.mod(vals + np.pi/2., np.pi) - np.pi/2.

                # is user requested axis in an order that is different from h5parm, we need to transpose
                if cmesh:
                    if soltab.getAxesNames().index(axesInPlot[0]) < soltab.getAxesNames().index(axesInPlot[1]):
                        vals = vals.T
                        weight = weight.T

                # unwrap if required
                if (soltab.getType() == 'phase' or soltab.getType() == 'scalarphase') and doUnwrap:
                    if len(axesInPlot) == 1:
                        vals = unwrap(vals)
                    else:
                        flags = np.array((weight == 0), dtype=bool)
                        if not (flags == True).all():
                            vals = unwrap_2d(vals, flags, coord[axesInPlot[0]], coord[axesInPlot[1]])

                dataCube[Ntab,Ncol] = vals
                sel1 = np.where(weight == 0.)
                sel2 = np.where(np.isnan(vals))
                if cmesh:
                    dataCube[Ntab,Ncol,sel1[0],sel1[1]] = np.ma.masked
                    dataCube[Ntab,Ncol,sel2[0],sel2[1]] = np.ma.masked
                else:
                    dataCube[Ntab,Ncol,sel1[0]] = np.ma.masked
                    dataCube[Ntab,Ncol,sel2[0]] = np.ma.masked

            soltab.selection = soltab2Selection
            ### end cycle on colors

        # if dataCube too large (> 500 MB) do not go parallel
        if np.array(dataCube).nbytes > 1024*1024*500:
            logging.debug('Big plot, parallel not possible.')
            _plot(Nplots, NColFig, figSize, markerSize, cmesh, axesInPlot, axisInTable, xvals, yvals, xlabelunit, ylabelunit, datatype, prefix+filename, titles, log, dataCube, minZ, maxZ, plotFlag, makeMovie, antCoords, None)
        else:
            mpm.put([Nplots, NColFig, figSize, markerSize, cmesh, axesInPlot, axisInTable, xvals, yvals, xlabelunit, ylabelunit, datatype, prefix+filename, titles, log, np.ma.copy(dataCube), minZ, maxZ, plotFlag, makeMovie, antCoords])
        if makeMovie: pngs.append(prefix+filename+'.png')

        soltab.selection = soltab1Selection
        ### end cycle on tables
    mpm.wait()

    if makeMovie:
        def long_substr(strings):
            """
            Find longest common substring
            """
            substr = ''
            if len(strings) > 1 and len(strings[0]) > 0:
                for i in range(len(strings[0])):
                    for j in range(len(strings[0])-i+1):
                        if j > len(substr) and all(strings[0][i:i+j] in x for x in strings):
                            substr = strings[0][i:i+j]
            return substr
        movieName = long_substr(pngs)
        assert movieName != '' # need a common prefix, use prefix keyword in case
        logging.info('Making movie: '+movieName)
        # make every movie last 20 sec, min one second per slide
        fps = np.ceil(len(pngs)/200.)
        ss="mencoder -ovc lavc -lavcopts vcodec=mpeg4:vpass=1:vbitrate=6160000:mbd=2:keyint=132:v4mv:vqmin=3:lumi_mask=0.07:dark_mask=0.2:"+\
                "mpeg_quant:scplx_mask=0.1:tcplx_mask=0.1:naq -mf type=png:fps="+str(fps)+" -nosound -o "+movieName.replace('__tmp__','')+".mpg mf://"+movieName+"*  > mencoder.log 2>&1"
        os.system(ss)
        #for png in pngs: os.system('rm '+png)

    return 0