Example #1
0
def calcProjectionVectors(R1,R2,norm=None):
    r1 = R1[:3]
    r2 = R2[:3]
    if not norm is None:
        NV = norm
    else:
        NV = np.cross(r1,r2)
    NV/= np.linalg.norm(NV)
    
    Zeros = np.isclose(NV,0.0)
    if np.sum(Zeros)==3:
        raise AttributeError('The two plane vectors are equivalen, {}, {}!'.format(r1,r2))
    
    
    if np.sum(Zeros) == 2 or np.sum(Zeros)==1: # Easy case where the two vectors are to be along the x, y, or z directions
        if Zeros[0] == True:
            V1 = np.array([1.0,0.0,0.0])
            V2 = np.cross(NV,V1)
        elif Zeros[1]:
            V1 = np.array([0.0,1.0,0.0])
            V2 = np.cross(NV,V1)
        else:
            V1 = np.array([0.0,0.0,1.0])
            V2 = np.cross(NV,V1)
    else: # The tricky case of all vectors having non-zero components.
        V1 = r1
        V2 = r2
            
    V1 = _tools.LengthOrder(V1)
    V2 = _tools.LengthOrder(V2)
    #for V in [V1,V2]: # Flip sign if needed
    #    maxArg = np.argmax(np.abs(V))
    #    V*=np.sign(V[maxArg])
    return V1,V2
Example #2
0
def createQEAxes(DataSet=None,axis=0,figure = None, projectionVector1 = None, projectionVector2 = None):
    """Function to create Q E plot

    Kwargs:

        - DataSet (DataSet): If provided and no projections vectors creates QE axis for main direction (default None)

        - axis (int): Whether to create axis 0 or 1 (projection vector 0 or orthogonal to this, default 0)

        - figure (figure): If provided, this is used to create the axis within (default None)

        - projectionVector1 (vec): Projection vector along which data is plotted. If not provided sample vector is used (default None)

        - projectionVector2 (vec): Projection vector orthogonal to data. If not provided sample vector is used (default None)


    """
    
    if projectionVector1 is None or projectionVector2 is None:
        v1 = DataSet.sample[0].projectionVector1
        v2 = DataSet.sample[0].projectionVector2
        angle = DataSet.sample[0].projectionAngle
        orientationMatrix = DataSet.sample[0].orientationMatrix
    else:
        v1 = np.array(projectionVector1)
        v2 = np.array(projectionVector2)
        
        if not np.all([x.shape==(3,) for x in [v1,v2]]) or not np.all([len(x.shape)==1 for x in [v1,v2]]):
            raise AttributeError('Provided vector(s) is not 3D: projectionVector1.shape={} or projectionVector2.shape={}'.format(v1.shape,v2.shape))
        angle = np.arccos(np.dot(v1,v2)/(np.linalg.norm(v1)*np.linalg.norm(v2)))
        orientationMatrix = np.ones(3)

    sample = copy.deepcopy(DataSet.sample)
    
    v1,v2 = sample[0].projectionVector1,sample[0].projectionVector2
    angle = np.sign(np.dot(np.cross(v1,v2),sample[0].planeNormal))*sample[0].projectionAngle
    
    v2Length = np.linalg.norm(v2)/np.linalg.norm(v1)
    projectionMatrix = np.linalg.inv(np.array([[1,0],[np.cos(angle)*v2Length,np.sin(angle)*v2Length]]).T)
    
    projectionVectorQX = np.dot(np.dot(projectionMatrix,[1,0]),np.array([v1,v2]))
    projectionVectorQY = np.dot(np.dot(projectionMatrix,[0,1]),np.array([v1,v2]))
    projectionVectorQX = _tools.LengthOrder(projectionVectorQX)
    projectionVectorQY = _tools.LengthOrder(projectionVectorQY)
    projectionVectorQXLength = np.linalg.norm(np.dot(orientationMatrix,projectionVectorQY))
    projectionVectorQYLength = np.linalg.norm(np.dot(orientationMatrix,projectionVectorQX))
    projectionVectorQXFormated = ', '.join(['{:.3f}'.format(x) for x in projectionVectorQX])
    projectionVectorQYFormated = ', '.join(['{:.3f}'.format(x) for x in projectionVectorQY])
    
    if axis == 0:
        projectionVectorLength = projectionVectorQYLength
        projectionVectorLengthORthogonal = projectionVectorQXLength
        projectionVectorFormated = projectionVectorQXFormated
        projectionVector = projectionVectorQX
        projectionVectorOrthogonal = projectionVectorQY
    elif axis == 1:
        projectionVectorLength = projectionVectorQXLength
        projectionVectorFormated = projectionVectorQYFormated
        projectionVectorLengthORthogonal = projectionVectorQYLength
        projectionVector = projectionVectorQY
        projectionVectorOrthogonal = projectionVectorQX
    else:
        raise AttributeError('Provided axis of {} is not allowed. Should be either 0 or 1.'.format(axis))

    if figure is None:
        
        figure = plt.figure(figsize=(7, 4))
    else:
        figure.clf()
    def inv_tr(l,x,y):
        return x*l,y
    
    def tr(l,x,y):
        return x/l,y
    
    if pythonVersion == 3:
        grid_locator1 = MultipleLocator(base=1.0) # Standard X ticks is multiple locator
        grid_helper = GridHelperCurveLinear((lambda x,y:inv_tr(projectionVectorLength,x,y), 
                                        lambda x,y:tr(projectionVectorLength,x,y)),grid_locator1=grid_locator1)
    
    else:
        grid_helper = GridHelperCurveLinear((lambda x,y:inv_tr(projectionVectorLength,x,y), 
                                        lambda x,y:tr(projectionVectorLength,x,y)))
    
    ax = SubplotHost(figure, 1, 1, 1, grid_helper=grid_helper)
    ax.sample = sample[0]

    figure.add_subplot(ax)
    #ax.set_aspect(1.)
    ax.grid(True, zorder=0)
    
    def calculateRLU(l,v1,x,y,v,step):
        return np.asarray(x)/l*v1+v*step, np.asarray(y)

    def format_coord(x,y): # pragma: no cover # x is H,K,L and y is  energy
        xformated = ', '.join(['{} = {}'.format(Y[0],Y[1]) for Y in zip(['h','k','l'],['{:.4f}'.format(X) for X in x])])
        return '{}, E={:.4f}'.format(xformated,y)
    
    
    ax.set_xlabel('{} [RLU]'.format(projectionVectorFormated))
    
    ax.set_ylabel('E [meV]')
    ax._length = projectionVectorLengthORthogonal
    ax._projectionVector = projectionVector 
    ax._projectionVectorOrthogonal = projectionVectorOrthogonal
    ax._step = 0.0
    ax.calculateRLU = lambda x,y: calculateRLU(projectionVectorLength,ax._projectionVector,x,y,ax._projectionVectorOrthogonal,ax._step)
    ax.format_coord = lambda x,y: format_coord(*ax.calculateRLU(x,y))


    if pythonVersion == 3:
        ax.forceGridUpdate = lambda:forceGridUpdate(ax)
        ax.xticks = 7

        def xAxisChanged(axis, forceUpdate=False):
            locator = axis._grid_helper.grid_finder.grid_locator1
            xlim = axis.get_xlim()
            xlimDiff = np.diff(xlim)
            if isinstance(locator,MultipleLocator):
                if hasattr(axis,'xBase'):
                    base = axis.xBase
                else:
                    base = calculateBase(locator,xlimDiff,axis.xticks)
                locator.set_params(base=base)
                
            elif isinstance(locator,MaxNLocator):
                if hasattr(axis,'xTicks'):
                    ticks = getattr(axis,'xTicks')
                else:
                    ticks = 7
                locator.set_params(nbins = ticks)
            else:
                return
            axis.forceGridUpdate()

        ax.callbacks.connect('xlim_changed', xAxisChanged)

        ax.callbacks.connect('draw_event',lambda ax: xAxisChanged(ax,forceUpdate=True))
        ax.xAxisChanged = lambda: xAxisChanged(ax,forceUpdate=True)


        @updateXAxisDecorator(ax=ax)
        def set_xticks_base(xBase=None,ax=ax):
            """Setter of the base x ticks to be used for plotting

            Kwargs:

                - xBase (float): Base of the tick marks (default automatic)

            """
            
                
            if not isinstance(ax._grid_helper.grid_finder.grid_locator1,MultipleLocator):
                l1 = MultipleLocator(base=xBase)
                ax._grid_helper.update_grid_finder(grid_locator1=l1)

            if xBase is None:
                if hasattr(ax,'xBase'):
                    delattr(ax,'xBase')
            else:
                ax.xBase = xBase

        @updateXAxisDecorator(ax=ax)
        def set_xticks_number(xNumber = None,ax=ax):
            """Setter of the number of x ticks to be used for plotting

            Kwargs:

                - xNumber (int): Number of x tick marks (default 7)

            """
            if xNumber is None:
                xNumber = 7

            if not isinstance(ax._grid_helper.grid_finder.grid_locator1,MaxNLocator):
                l1 = MaxNLocator(nbins=xNumber)
                ax._grid_helper.update_grid_finder(grid_locator1=l1)
            ax.xTicks = xNumber


        ax.set_xticks_base = set_xticks_base
        ax.set_xticks_number = set_xticks_number


    return ax
    def __init__(self, data, bins, sample, log=False, *args, **kwargs):
        annoyingFigure = plt.gca().get_figure()
        if not len(annoyingFigure.get_children()
                   ) <= 1:  # If less than or equal to 1 child figure is empty
            plt.close(
                annoyingFigure
            )  # Mega hack to save the day (close annoying mpl figure....... not a fan). MDT = magic don't touch
        pg.setConfigOption('background', 'w')
        pg.setConfigOption('foreground', 'k')
        super().__init__(*args, **kwargs)
        self.l = QtGui.QGridLayout()
        self.setLayout(self.l)

        # Add plotItem to allow for axes

        self.imv1 = CustomImageView(view=pg.PlotItem())
        self.imv2 = pg.ImageView(view=pg.PlotItem())

        #Insert widgets into layout
        self.l.addWidget(self.imv1, 0, 0)
        self.l.addWidget(self.imv2, 1, 0)

        # Create the region-of-interest line segment
        self.roi = pg.ROI([1, 0], [1, 1], pen='r', resizable=True)
        self.roi.setSize([1, 0.1])
        self.roi.addScaleHandle(
            [0.5, 1],
            [0.5, 0.5],
        )
        self.roi.addScaleHandle([0, 0.5], [0.5, 0.5])
        self.roi.addRotateHandle([1.0, 1.0], [0.5, 0.5])

        # Change color of roi markers
        handleColor = QtGui.QColor(255, 102, 0)
        for handle in self.roi.getHandles():
            handle.pen.setColor(handleColor)

        self.imv1.addItem(self.roi)

        self.Data, self.bins = data, bins

        if len(data) == 4:
            with warnings.catch_warnings():
                warnings.simplefilter("ignore")
                self.data = np.divide(self.Data[0] * self.Data[-1],
                                      self.Data[1] * self.Data[2])
                self.data[self.Data[-1] == 0] = -np.nanmin(
                    self.data[self.data != 0])
        else:
            self.data = self.Data

        # set unmeasured areas to negative value
        if log:
            self.data = np.log10(self.data + 1e-20)

        # transpose data to comply with viewer requirements
        self.data = self.data.transpose((2, 0, 1))

        # Extract the sample
        self.sample = sample
        # Calculate the projection directions
        self.axes = np.array(
            [self.sample.calculateQxQyToHKL(*X) for X in self.sample.RotMat])

        ## Display the data
        self.imv1.setImage(self.data,
                           xvals=self.bins[2][0, 0, :],
                           levels=(-1e-7, 1e-5),
                           autoLevels=False)

        self.imv1.setHistogramRange(-8.5e-8, 1e-5)
        # unlock aspect ratio
        self.imv1.view.setAspectLocked(False)

        # Generate and add correct labels to axes
        self.xaxis = self.imv1.view.axes['bottom']['item']
        self.yaxis = self.imv1.view.axes['left']['item']

        self.xlabel = _tools.generateLabel(_tools.LengthOrder(self.axes[0]))
        self.ylabel = _tools.generateLabel(_tools.LengthOrder(self.axes[1]))

        self.xaxis.setLabel(text=self.xlabel, units='RLU')
        self.yaxis.setLabel(text=self.ylabel, units='RLU')

        # Setup color map for main window
        self.setupColorscale(self.imv1)

        # Calcualte scaling and offsets between image view and RLU axes
        self.qxRange = np.diff(self.bins[0][[0, -1], 0, 0])
        self.qyRange = np.diff(self.bins[1][0, [0, -1], 0])
        self.ERange = np.diff(self.bins[2][0, 0, [0, -1]])

        self.qxScale = self.qxRange * np.linalg.norm(
            self.axes[0]) / self.bins[0].shape[0]
        self.qyScale = self.qyRange * np.linalg.norm(
            self.axes[1]) / self.bins[0].shape[1]
        self.EScale = self.ERange / self.bins[0].shape[2]

        self.qxCenter = self.bins[0][0, 0, 0] * np.linalg.norm(self.axes[0])
        self.qyCenter = self.bins[1][0, 0, 0] * np.linalg.norm(self.axes[1])
        self.ECenter = self.bins[2][0, 0, 0]

        # Apply scaling and translation
        img1Item = self.imv1.getImageItem()
        img1Item.scale(self.qxScale, self.qyScale)
        img1Item.translate(self.qxCenter / self.qxScale,
                           self.qyCenter / self.qyScale)
        # Un-invert yaxis
        self.imv1.view.getViewBox().invertY(False)
        self.imv1.view.autoRange(True)
        self.imv1.view.setAutoVisible(x=True, y=True)

        # Add labels colormap for cut window
        self.imv2.view.setLabel("left", "Energy", units='meV')
        self.imv2.view.setLabel("bottom", "HKL", units='RLU')
        self.setupColorscale(self.imv2)

        # Hide all unneeded menus and uninvert yaxes
        self.imv2.ui.roiBtn.hide()
        self.imv2.ui.menuBtn.hide()
        self.imv2.view.getViewBox().invertY(False)

        # Extract projection matrix used for position calculation along cut
        self.projectionMatrix = self.axes
        self.projectionMatrix[0] *= 1 / np.linalg.norm(
            self.projectionMatrix[0])
        self.projectionMatrix[1] *= 1 / np.linalg.norm(
            self.projectionMatrix[1])
        self.xaxis2 = self.imv2.view.axes['bottom']['item']
        self.yaxis2 = self.imv2.view.axes['left']['item']

        self.xaxis2.tickStrings = lambda values, scale, spacing: self.XtickStrings(
            self.roi, values, scale, spacing)
        self.yaxis2.tickStrings = lambda values, scale, spacing: self.YtickStrings(
            values, scale, spacing)

        # Create function to be called when cut changes

        # Connect update-function to correct slot
        self.roi.sigRegionChanged.connect(self.update)

        # Call update for initial position
        self.update()