def curvelinear_test1(fig): """ grid for custom transform. """ def tr(x, y): x, y = np.asarray(x), np.asarray(y) return x, y - x def inv_tr(x, y): x, y = np.asarray(x), np.asarray(y) return x, y + x grid_helper = GridHelperCurveLinear((tr, inv_tr)) ax1 = Subplot(fig, 1, 2, 1, grid_helper=grid_helper) # ax1 will have a ticks and gridlines defined by the given # transform (+ transData of the Axes). Note that the transform of # the Axes itself (i.e., transData) is not affected by the given # transform. fig.add_subplot(ax1) xx, yy = tr([3, 6], [5.0, 10.]) ax1.plot(xx, yy, linewidth=2.0) ax1.set_aspect(1.) ax1.set_xlim(0, 10.) ax1.set_ylim(0, 10.) ax1.axis["t"] = ax1.new_floating_axis(0, 3.) ax1.axis["t2"] = ax1.new_floating_axis(1, 7.) ax1.grid(True, zorder=0)
def plotCorrelation(tauArray,kappaMatrix,kappaLower=None,kappaUpper=None,CI=None,amplify=1): """Plots Pearson Correlation Coefficient K(t,tau) with rotated axis to indicate absolute t, and relative time shift tau, between two signals x(t),y(t). Specified matrix has to be square with values -1 < p < +1 with corresponding time array giving the absolute time, t of the centers of each correlated window.""" # defining tranformation for relative time shifts def R(x, y): x, y = asarray(x), asarray(y) #return x,y return (2*x - y)/2, (y + 2*x)/2 def Rt(x, y): x, y = asarray(x), asarray(y) #return x,y return x + y, x - y # create figure with rotated axes fig = figure(figsize=(10, 10),frameon=False) grid_locator = angle_helper.LocatorDMS(20) grid_helper = GridHelperCurveLinear((R, Rt), grid_locator1=grid_locator, grid_locator2=grid_locator) ax = Subplot(fig, 1, 1, 1, grid_helper=grid_helper) fig.add_subplot(ax);ax.axis('off'); # copying over matrix K = array(kappaMatrix) # zero out correlations if confidence intervals overlap zero if all(kappaLower != None) and all(kappaUpper != None) : K[ (kappaLower<0) * (0<kappaUpper) ] = 0 # zero out statistically insignificant correlations if all(CI != None) : K[ abs(kappaMatrix) < CI ] = 0 # display pearson correlation matrix with +ive in red and -ive in blue ax.imshow(K,cmap="RdBu_r",interpolation="none",origin="bottom", extent = (tauArray[0],tauArray[-1],tauArray[0],tauArray[-1]),vmin=-1.0/amplify,vmax=1.0/amplify) # display rotated axes time,t and time delay,tau ax.axis["tau"] = tau = ax.new_floating_axis(0,0) ax.axis["t"] = t = ax.new_floating_axis(1,0) # setting axes options ax.set_xlim(tauArray[0],tauArray[-1]) ax.set_ylim(tauArray[0],tauArray[-1]) ax.grid(which="both") ax.set_aspect(1) return fig
def setup_axes1(self, fig, T_ticks, subplotshape=None): """ A simple one. """ deg = -45. self.tr = Affine2D().rotate_deg(deg) theta_ticks = [] #np.arange(theta_min, theta_max, d_T) grid_helper = GridHelperCurveLinear( self.tr, grid_locator1=FixedLocator(T_ticks), grid_locator2=FixedLocator(theta_ticks)) if subplotshape is None: subplotshape = (1, 1, 1) ax1 = Subplot(fig, *subplotshape, grid_helper=grid_helper) # ax1 will have a ticks and gridlines defined by the given # transform (+ transData of the Axes). Note that the transform of # the Axes itself (i.e., transData) is not affected by the given # transform. fig.add_subplot(ax1) # SW, SE, NE, NW corners = np.array([[-25., -20.], [30., 40.], [-40., 120.], [-105., 60.]]) corners_t = self._tf(corners[:, 0], corners[:, 1]) # ax1.set_aspect(1.) x_min, x_max = self.x_range ax1.set_xlim(x_min, x_max) ax1.set_ylim(*self.y_range) ax1.set_xlabel('Temperature [C]') ax1.set_aspect(1) #ax1.axis["t"]=ax1.new_floating_axis(0, 0.) #T_axis = ax1.axis['t'] #theta_axis = ax1.axis["t2"]=ax1.new_floating_axis(1, 0.) # plot.draw() # plot.show() self.ax1 = ax1
def setup_axes1(self, fig, T_ticks, subplotshape=None): """ A simple one. """ deg = -45. self.tr = Affine2D().rotate_deg(deg) theta_ticks = [] #np.arange(theta_min, theta_max, d_T) grid_helper = GridHelperCurveLinear(self.tr, grid_locator1=FixedLocator(T_ticks), grid_locator2=FixedLocator(theta_ticks)) if subplotshape is None: subplotshape = (1,1,1) ax1 = Subplot(fig, *subplotshape, grid_helper=grid_helper) # ax1 will have a ticks and gridlines defined by the given # transform (+ transData of the Axes). Note that the transform of # the Axes itself (i.e., transData) is not affected by the given # transform. fig.add_subplot(ax1) # SW, SE, NE, NW corners = np.array([[-25., -20.], [30., 40.], [-40., 120.], [-105., 60.]]) corners_t = self._tf(corners[:,0], corners[:,1]) # ax1.set_aspect(1.) x_min, x_max = self.x_range ax1.set_xlim(x_min, x_max) ax1.set_ylim(*self.y_range) ax1.set_xlabel('Temperature [C]') ax1.set_aspect(1) #ax1.axis["t"]=ax1.new_floating_axis(0, 0.) #T_axis = ax1.axis['t'] #theta_axis = ax1.axis["t2"]=ax1.new_floating_axis(1, 0.) # plot.draw() # plot.show() self.ax1 = ax1
def curvelinear_test1(fig): """ grid for custom transform. """ def tr(x, y): x, y = np.asarray(x), np.asarray(y) return x, y-x def inv_tr(x,y): x, y = np.asarray(x), np.asarray(y) return x, y+x grid_helper = GridHelperCurveLinear((tr, inv_tr)) ax1 = Subplot(fig, 1, 2, 1, grid_helper=grid_helper) fig.add_subplot(ax1) xx, yy = tr([3, 6], [5.0, 10.]) ax1.plot(xx, yy) ax1.set_aspect(1.) ax1.set_xlim(0, 10.) ax1.set_ylim(0, 10.) ax1.axis["t"]=ax1.new_floating_axis(0, 3.) ax1.axis["t2"]=ax1.new_floating_axis(1, 7.) ax1.grid(True)
def curvelinear_test1(fig): """ Grid for custom transform. """ def tr(x, y): x, y = numpy.asarray(x), numpy.asarray(y) return x, y - (2 * x) # return x + (5 * y), (7 * y) + (3 * x) def inv_tr(x, y): x, y = numpy.asarray(x), numpy.asarray(y) return x, y + (2 * x) grid_helper = GridHelperCurveLinear((tr, inv_tr)) ax1 = Subplot(fig, 1, 1, 1, grid_helper=grid_helper) # ax1 will have a ticks and gridlines defined by the given # transform (+ transData of the Axes). Note that the transform of # the Axes itself (i.e., transData) is not affected by the given # transform. fig.add_subplot(ax1) xx, yy = tr([0, 1], [0, 2]) ax1.plot(xx, yy, linewidth=2.0) ax1.set_aspect(1) ax1.set_xlim(-3, 3) ax1.set_ylim(-3, 3) ax1.axis["t"] = ax1.new_floating_axis( 0, 0 ) # first argument appears to be slope, second argument appears to be starting point on vertical ax1.axis["t2"] = ax1.new_floating_axis(1, 0) ax1.axhline(y=0, color='r') ax1.axvline(x=0, color='r') ax1.grid(True, zorder=0)
class DGSPlannerGUI(QtGui.QWidget): def __init__(self, ol=None, parent=None): # pylint: disable=unused-argument,super-on-old-class super(DGSPlannerGUI, self).__init__(parent) #OrientedLattice if ValidateOL(ol): self.ol = ol else: self.ol = mantid.geometry.OrientedLattice() self.masterDict = dict() #holds info about instrument and ranges self.updatedInstrument = False self.updatedOL = False self.wg = None #workspace group self.instrumentWidget = InstrumentSetupWidget.InstrumentSetupWidget( self) self.setLayout(QtGui.QHBoxLayout()) controlLayout = QtGui.QVBoxLayout() controlLayout.addWidget(self.instrumentWidget) self.ublayout = QtGui.QHBoxLayout() self.classic = ClassicUBInputWidget.ClassicUBInputWidget(self.ol) self.ublayout.addWidget(self.classic, alignment=QtCore.Qt.AlignTop, stretch=1) self.matrix = MatrixUBInputWidget.MatrixUBInputWidget(self.ol) self.ublayout.addWidget(self.matrix, alignment=QtCore.Qt.AlignTop, stretch=1) controlLayout.addLayout(self.ublayout) self.dimensionWidget = DimensionSelectorWidget.DimensionSelectorWidget( self) controlLayout.addWidget(self.dimensionWidget) plotControlLayout = QtGui.QGridLayout() self.plotButton = QtGui.QPushButton("Plot", self) self.oplotButton = QtGui.QPushButton("Overplot", self) self.helpButton = QtGui.QPushButton("?", self) self.colorLabel = QtGui.QLabel('Color by angle', self) self.colorButton = QtGui.QCheckBox(self) self.colorButton.toggle() self.aspectLabel = QtGui.QLabel('Aspect ratio 1:1', self) self.aspectButton = QtGui.QCheckBox(self) self.saveButton = QtGui.QPushButton("Save Figure", self) plotControlLayout.addWidget(self.plotButton, 0, 0) plotControlLayout.addWidget(self.oplotButton, 0, 1) plotControlLayout.addWidget(self.colorLabel, 0, 2, QtCore.Qt.AlignRight) plotControlLayout.addWidget(self.colorButton, 0, 3) plotControlLayout.addWidget(self.aspectLabel, 0, 4, QtCore.Qt.AlignRight) plotControlLayout.addWidget(self.aspectButton, 0, 5) plotControlLayout.addWidget(self.helpButton, 0, 6) plotControlLayout.addWidget(self.saveButton, 0, 7) controlLayout.addLayout(plotControlLayout) self.layout().addLayout(controlLayout) #figure self.figure = Figure() self.figure.patch.set_facecolor('white') self.canvas = FigureCanvas(self.figure) self.grid_helper = GridHelperCurveLinear((self.tr, self.inv_tr)) self.trajfig = Subplot(self.figure, 1, 1, 1, grid_helper=self.grid_helper) self.trajfig.hold(True) self.figure.add_subplot(self.trajfig) self.layout().addWidget(self.canvas) self.needToClear = False self.saveDir = '' #connections self.matrix.UBmodel.changed.connect(self.updateUB) self.matrix.UBmodel.changed.connect(self.classic.updateOL) self.classic.changed.connect(self.matrix.UBmodel.updateOL) self.classic.changed.connect(self.updateUB) self.instrumentWidget.changed.connect(self.updateParams) self.dimensionWidget.changed.connect(self.updateParams) self.plotButton.clicked.connect(self.updateFigure) self.oplotButton.clicked.connect(self.updateFigure) self.helpButton.clicked.connect(self.help) self.saveButton.clicked.connect(self.save) #force an update of values self.instrumentWidget.updateAll() self.dimensionWidget.updateChanges() #help self.assistantProcess = QtCore.QProcess(self) # pylint: disable=protected-access self.collectionFile = os.path.join(mantid._bindir, '../docs/qthelp/MantidProject.qhc') version = ".".join(mantid.__version__.split(".")[:2]) self.qtUrl = 'qthelp://org.sphinx.mantidproject.' + version + '/doc/interfaces/DGSPlanner.html' self.externalUrl = 'http://docs.mantidproject.org/nightly/interfaces/DGSPlanner.html' #control for cancel button self.iterations = 0 self.progress_canceled = False @QtCore.pyqtSlot(mantid.geometry.OrientedLattice) def updateUB(self, ol): self.ol = ol self.updatedOL = True self.trajfig.clear() @QtCore.pyqtSlot(dict) def updateParams(self, d): if self.sender() is self.instrumentWidget: self.updatedInstrument = True if d.has_key('dimBasis') and self.masterDict.has_key( 'dimBasis') and d['dimBasis'] != self.masterDict['dimBasis']: self.needToClear = True if d.has_key('dimIndex') and self.masterDict.has_key( 'dimIndex') and d['dimIndex'] != self.masterDict['dimIndex']: self.needToClear = True self.masterDict.update(copy.deepcopy(d)) def help(self): try: import pymantidplot pymantidplot.proxies.showCustomInterfaceHelp('DGSPlanner') except ImportError: self.assistantProcess.close() self.assistantProcess.waitForFinished() helpapp = QtCore.QLibraryInfo.location( QtCore.QLibraryInfo.BinariesPath) + QtCore.QDir.separator() helpapp += 'assistant' args = [ '-enableRemoteControl', '-collectionFile', self.collectionFile, '-showUrl', self.qtUrl ] if os.path.isfile(helpapp): self.assistantProcess.close() self.assistantProcess.waitForFinished() self.assistantProcess.start(helpapp, args) else: QtGui.QDesktopServices.openUrl(QtCore.QUrl(self.externalUrl)) def closeEvent(self, event): self.assistantProcess.close() self.assistantProcess.waitForFinished() event.accept() # pylint: disable=too-many-locals def updateFigure(self): # pylint: disable=too-many-branches if self.updatedInstrument or self.progress_canceled: self.progress_canceled = False #get goniometer settings first gonioAxis0values = numpy.arange( self.masterDict['gonioMinvals'][0], self.masterDict['gonioMaxvals'][0] + 0.1 * self.masterDict['gonioSteps'][0], self.masterDict['gonioSteps'][0]) gonioAxis1values = numpy.arange( self.masterDict['gonioMinvals'][1], self.masterDict['gonioMaxvals'][1] + 0.1 * self.masterDict['gonioSteps'][1], self.masterDict['gonioSteps'][1]) gonioAxis2values = numpy.arange( self.masterDict['gonioMinvals'][2], self.masterDict['gonioMaxvals'][2] + 0.1 * self.masterDict['gonioSteps'][2], self.masterDict['gonioSteps'][2]) self.iterations = len(gonioAxis0values) * len( gonioAxis1values) * len(gonioAxis2values) if self.iterations > 10: reply = QtGui.QMessageBox.warning( self, 'Goniometer', "More than 10 goniometer settings. This might be long.\n" "Are you sure you want to proceed?", QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.No: return if self.wg != None: mantid.simpleapi.DeleteWorkspace(self.wg) mantid.simpleapi.LoadEmptyInstrument( mantid.api.ExperimentInfo.getInstrumentFilename( self.masterDict['instrument']), OutputWorkspace="__temp_instrument") if self.masterDict['instrument'] == 'HYSPEC': mantid.simpleapi.AddSampleLog(Workspace="__temp_instrument", LogName='msd', LogText='1798.5', LogType='Number Series') mantid.simpleapi.AddSampleLog(Workspace="__temp_instrument", LogName='s2', LogText=str( self.masterDict['S2']), LogType='Number Series') mantid.simpleapi.LoadInstrument(Workspace="__temp_instrument", RewriteSpectraMap=True, InstrumentName="HYSPEC") #masking if self.masterDict.has_key('maskFilename') and len( self.masterDict['maskFilename'].strip()) > 0: try: __maskWS = mantid.simpleapi.Load( self.masterDict['maskFilename']) mantid.simpleapi.MaskDetectors( Workspace="__temp_instrument", MaskedWorkspace=__maskWS) except (ValueError, RuntimeError) as e: reply = QtGui.QMessageBox.critical( self, 'Error', "The following error has occured in loading the mask:\n" + str(e) + "\nDo you want to continue without mask?", QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.No: return if self.masterDict['makeFast']: sp = range( mantid.mtd["__temp_instrument"].getNumberHistograms()) tomask = sp[::4] + sp[1::4] + sp[2::4] mantid.simpleapi.MaskDetectors("__temp_instrument", SpectraList=tomask) i = 0 groupingStrings = [] progressDialog = QtGui.QProgressDialog(self) progressDialog.setMinimumDuration(0) progressDialog.setCancelButtonText("&Cancel") progressDialog.setRange(0, self.iterations) progressDialog.setWindowTitle("DGSPlanner progress") for g0 in gonioAxis0values: for g1 in gonioAxis1values: for g2 in gonioAxis2values: name = "__temp_instrument" + str(i) i += 1 progressDialog.setValue(i) progressDialog.setLabelText( "Creating workspace %d of %d..." % (i, self.iterations)) QtGui.qApp.processEvents() if progressDialog.wasCanceled(): self.progress_canceled = True progressDialog.close() return groupingStrings.append(name) mantid.simpleapi.CloneWorkspace("__temp_instrument", OutputWorkspace=name) mantid.simpleapi.SetGoniometer( Workspace=name, Axis0=str(g0) + "," + self.masterDict['gonioDirs'][0] + "," + str(self.masterDict['gonioSenses'][0]), Axis1=str(g1) + "," + self.masterDict['gonioDirs'][1] + "," + str(self.masterDict['gonioSenses'][1]), Axis2=str(g2) + "," + self.masterDict['gonioDirs'][2] + "," + str(self.masterDict['gonioSenses'][2])) progressDialog.close() mantid.simpleapi.DeleteWorkspace("__temp_instrument") self.wg = mantid.simpleapi.GroupWorkspaces( groupingStrings, OutputWorkspace="__temp_instrument") self.updatedInstrument = False #set the UB if self.updatedOL or not self.wg[0].sample().hasOrientedLattice(): mantid.simpleapi.SetUB(self.wg, UB=self.ol.getUB()) self.updatedOL = False #calculate coverage dimensions = ['Q1', 'Q2', 'Q3', 'DeltaE'] progressDialog = QtGui.QProgressDialog(self) progressDialog.setMinimumDuration(0) progressDialog.setCancelButtonText("&Cancel") progressDialog.setRange(0, self.iterations) progressDialog.setWindowTitle("DGSPlanner progress") for i in range(self.iterations): progressDialog.setValue(i) progressDialog.setLabelText("Calculating orientation %d of %d..." % (i, self.iterations)) QtGui.qApp.processEvents() if progressDialog.wasCanceled(): self.progress_canceled = True progressDialog.close() return __mdws = mantid.simpleapi.CalculateCoverageDGS( self.wg[i], Q1Basis=self.masterDict['dimBasis'][0], Q2Basis=self.masterDict['dimBasis'][1], Q3Basis=self.masterDict['dimBasis'][2], IncidentEnergy=self.masterDict['Ei'], Dimension1=dimensions[self.masterDict['dimIndex'][0]], Dimension1Min=float2Input(self.masterDict['dimMin'][0]), Dimension1Max=float2Input(self.masterDict['dimMax'][0]), Dimension1Step=float2Input(self.masterDict['dimStep'][0]), Dimension2=dimensions[self.masterDict['dimIndex'][1]], Dimension2Min=float2Input(self.masterDict['dimMin'][1]), Dimension2Max=float2Input(self.masterDict['dimMax'][1]), Dimension2Step=float2Input(self.masterDict['dimStep'][1]), Dimension3=dimensions[self.masterDict['dimIndex'][2]], Dimension3Min=float2Input(self.masterDict['dimMin'][2]), Dimension3Max=float2Input(self.masterDict['dimMax'][2]), Dimension4=dimensions[self.masterDict['dimIndex'][3]], Dimension4Min=float2Input(self.masterDict['dimMin'][3]), Dimension4Max=float2Input(self.masterDict['dimMax'][3])) if i == 0: intensity = __mdws.getSignalArray( )[:, :, 0, 0] * 1. #to make it writeable else: if self.colorButton.isChecked(): tempintensity = __mdws.getSignalArray()[:, :, 0, 0] intensity[numpy.where(tempintensity > 0)] = i + 1. else: tempintensity = __mdws.getSignalArray()[:, :, 0, 0] intensity[numpy.where(tempintensity > 0)] = 1. progressDialog.close() x = numpy.linspace( __mdws.getDimension(0).getMinimum(), __mdws.getDimension(0).getMaximum(), intensity.shape[0]) y = numpy.linspace( __mdws.getDimension(1).getMinimum(), __mdws.getDimension(1).getMaximum(), intensity.shape[1]) Y, X = numpy.meshgrid(y, x) xx, yy = self.tr(X, Y) Z = numpy.ma.masked_array(intensity, intensity == 0) Z = Z[:-1, :-1] #plotting if self.sender() is self.plotButton or self.needToClear: self.figure.clear() self.trajfig.clear() self.figure.add_subplot(self.trajfig) self.needToClear = False self.trajfig.pcolorfast(xx, yy, Z) if self.aspectButton.isChecked(): self.trajfig.set_aspect(1.) else: self.trajfig.set_aspect('auto') self.trajfig.set_xlabel(self.masterDict['dimNames'][0]) self.trajfig.set_ylabel(self.masterDict['dimNames'][1]) self.trajfig.grid(True) self.canvas.draw() mantid.simpleapi.DeleteWorkspace(__mdws) def save(self): fileName = str( QtGui.QFileDialog.getSaveFileName(self, 'Save Plot', self.saveDir, '*.png')) data = "Instrument " + self.masterDict['instrument'] + '\n' if self.masterDict['instrument'] == 'HYSPEC': data += "S2 = " + str(self.masterDict['S2']) + '\n' data += "Ei = " + str(self.masterDict['Ei']) + ' meV\n' data += "Goniometer values:\n" gonioAxis0values = numpy.arange( self.masterDict['gonioMinvals'][0], self.masterDict['gonioMaxvals'][0] + 0.1 * self.masterDict['gonioSteps'][0], self.masterDict['gonioSteps'][0]) gonioAxis1values = numpy.arange( self.masterDict['gonioMinvals'][1], self.masterDict['gonioMaxvals'][1] + 0.1 * self.masterDict['gonioSteps'][1], self.masterDict['gonioSteps'][1]) gonioAxis2values = numpy.arange( self.masterDict['gonioMinvals'][2], self.masterDict['gonioMaxvals'][2] + 0.1 * self.masterDict['gonioSteps'][2], self.masterDict['gonioSteps'][2]) for g0 in gonioAxis0values: for g1 in gonioAxis1values: for g2 in gonioAxis2values: data += " " + self.masterDict['gonioLabels'][ 0] + " = " + str(g0) data += " " + self.masterDict['gonioLabels'][ 1] + " = " + str(g1) data += " " + self.masterDict['gonioLabels'][ 2] + " = " + str(g2) + '\n' data += "Lattice parameters:\n" data += " a = " + str(self.ol.a()) + " b = " + str( self.ol.b()) + " c = " + str(self.ol.c()) + '\n' data += " alpha = " + str(self.ol.alpha()) + " beta = " + str( self.ol.beta()) + " gamma = " + str(self.ol.gamma()) + '\n' data += "Orientation vectors:\n" data += " u = " + str(self.ol.getuVector()) + '\n' data += " v = " + str(self.ol.getvVector()) + '\n' data+="Integrated "+self.masterDict['dimNames'][2]+" between "+\ str(self.masterDict['dimMin'][2])+" and "+str(self.masterDict['dimMax'][2])+'\n' data+="Integrated "+self.masterDict['dimNames'][3]+" between "+\ str(self.masterDict['dimMin'][3])+" and "+str(self.masterDict['dimMax'][3])+'\n' info = self.figure.text(0.2, 0, data, verticalalignment='top') self.figure.savefig(fileName, bbox_inches='tight', additional_artists=info) self.saveDir = os.path.dirname(fileName) def tr(self, x, y): x, y = numpy.asarray(x), numpy.asarray(y) #one of the axes is energy if self.masterDict['dimIndex'][0] == 3 or self.masterDict['dimIndex'][ 1] == 3: return x, y else: h1, k1, l1 = (float(temp) for temp in self.masterDict['dimBasis'][ self.masterDict['dimIndex'][0]].split(',')) h2, k2, l2 = (float(temp) for temp in self.masterDict['dimBasis'][ self.masterDict['dimIndex'][1]].split(',')) angle = numpy.radians(self.ol.recAngle(h1, k1, l1, h2, k2, l2)) return 1. * x + numpy.cos(angle) * y, numpy.sin(angle) * y def inv_tr(self, x, y): x, y = numpy.asarray(x), numpy.asarray(y) #one of the axes is energy if self.masterDict['dimIndex'][0] == 3 or self.masterDict['dimIndex'][ 1] == 3: return x, y else: h1, k1, l1 = (float(temp) for temp in self.masterDict['dimBasis'][ self.masterDict['dimIndex'][0]].split(',')) h2, k2, l2 = (float(temp) for temp in self.masterDict['dimBasis'][ self.masterDict['dimIndex'][1]].split(',')) angle = numpy.radians(self.ol.recAngle(h1, k1, l1, h2, k2, l2)) return 1. * x - y / numpy.tan(angle), y / numpy.sin(angle)
class DGSPlannerGUI(QtWidgets.QWidget): def __init__(self, parent=None, window_flags=None, ol=None): # pylint: disable=unused-argument,super-on-old-class super(DGSPlannerGUI, self).__init__(parent) if window_flags: self.setWindowFlags(window_flags) # OrientedLattice if ValidateOL(ol): self.ol = ol else: self.ol = mantid.geometry.OrientedLattice() self.masterDict = dict() # holds info about instrument and ranges self.updatedInstrument = False self.instrumentWAND = False self.updatedOL = False self.wg = None # workspace group self.instrumentWidget = InstrumentSetupWidget.InstrumentSetupWidget( self) self.setLayout(QtWidgets.QHBoxLayout()) controlLayout = QtWidgets.QVBoxLayout() geometryBox = QtWidgets.QGroupBox("Instrument Geometry") plotBox = QtWidgets.QGroupBox("Plot Axes") geometryBoxLayout = QtWidgets.QVBoxLayout() geometryBoxLayout.addWidget(self.instrumentWidget) geometryBox.setLayout(geometryBoxLayout) controlLayout.addWidget(geometryBox) self.ublayout = QtWidgets.QHBoxLayout() self.classic = ClassicUBInputWidget.ClassicUBInputWidget(self.ol) self.ublayout.addWidget(self.classic, alignment=QtCore.Qt.AlignTop, stretch=1) self.matrix = MatrixUBInputWidget.MatrixUBInputWidget(self.ol) self.ublayout.addWidget(self.matrix, alignment=QtCore.Qt.AlignTop, stretch=1) sampleBox = QtWidgets.QGroupBox("Sample") sampleBox.setLayout(self.ublayout) controlLayout.addWidget(sampleBox) self.dimensionWidget = DimensionSelectorWidget.DimensionSelectorWidget( self) plotBoxLayout = QtWidgets.QVBoxLayout() plotBoxLayout.addWidget(self.dimensionWidget) plotControlLayout = QtWidgets.QGridLayout() self.plotButton = QtWidgets.QPushButton("Plot", self) self.oplotButton = QtWidgets.QPushButton("Overplot", self) self.helpButton = QtWidgets.QPushButton("?", self) self.colorLabel = QtWidgets.QLabel('Color by angle', self) self.colorButton = QtWidgets.QCheckBox(self) self.colorButton.toggle() self.aspectLabel = QtWidgets.QLabel('Aspect ratio 1:1', self) self.aspectButton = QtWidgets.QCheckBox(self) self.saveButton = QtWidgets.QPushButton("Save Figure", self) plotControlLayout.addWidget(self.plotButton, 0, 0) plotControlLayout.addWidget(self.oplotButton, 0, 1) plotControlLayout.addWidget(self.colorLabel, 0, 2, QtCore.Qt.AlignRight) plotControlLayout.addWidget(self.colorButton, 0, 3) plotControlLayout.addWidget(self.aspectLabel, 0, 4, QtCore.Qt.AlignRight) plotControlLayout.addWidget(self.aspectButton, 0, 5) plotControlLayout.addWidget(self.helpButton, 0, 6) plotControlLayout.addWidget(self.saveButton, 0, 7) plotBoxLayout.addLayout(plotControlLayout) plotBox = QtWidgets.QGroupBox("Plot Axes") plotBox.setLayout(plotBoxLayout) controlLayout.addWidget(plotBox) self.layout().addLayout(controlLayout) # figure self.figure = Figure() self.figure.patch.set_facecolor('white') self.canvas = FigureCanvas(self.figure) self.grid_helper = GridHelperCurveLinear((self.tr, self.inv_tr)) self.trajfig = Subplot(self.figure, 1, 1, 1, grid_helper=self.grid_helper) if matplotlib.compare_versions('2.1.0', matplotlib.__version__): self.trajfig.hold( True) # hold is deprecated since 2.1.0, true by default self.figure.add_subplot(self.trajfig) self.toolbar = MantidNavigationToolbar(self.canvas, self) figureLayout = QtWidgets.QVBoxLayout() figureLayout.addWidget(self.toolbar, 0) figureLayout.addWidget(self.canvas, 1) self.layout().addLayout(figureLayout) self.needToClear = False self.saveDir = '' # connections self.matrix.UBmodel.changed.connect(self.updateUB) self.matrix.UBmodel.changed.connect(self.classic.updateOL) self.classic.changed.connect(self.matrix.UBmodel.updateOL) self.classic.changed.connect(self.updateUB) self.instrumentWidget.changed.connect(self.updateParams) self.instrumentWidget.getInstrumentComboBox().activated[str].connect( self.instrumentUpdateEvent) self.instrumentWidget.getEditEi().textChanged.connect( self.eiWavelengthUpdateEvent) self.dimensionWidget.changed.connect(self.updateParams) self.plotButton.clicked.connect(self.updateFigure) self.oplotButton.clicked.connect(self.updateFigure) self.helpButton.clicked.connect(self.help) self.saveButton.clicked.connect(self.save) # force an update of values self.instrumentWidget.updateAll() self.dimensionWidget.updateChanges() # help self.assistant_process = QtCore.QProcess(self) # pylint: disable=protected-access self.mantidplot_name = 'DGS Planner' # control for cancel button self.iterations = 0 self.progress_canceled = False # register startup mantid.UsageService.registerFeatureUsage( mantid.kernel.FeatureType.Interface, "DGSPlanner", False) @QtCore.Slot(mantid.geometry.OrientedLattice) def updateUB(self, ol): self.ol = ol self.updatedOL = True self.trajfig.clear() def eiWavelengthUpdateEvent(self): if self.masterDict['instrument'] == 'WAND\u00B2': ei = UnitConversion.run('Wavelength', 'Energy', self.masterDict['Ei'], 0, 0, 0, Elastic, 0) offset = ei * 0.01 lowerBound = -offset upperBound = offset self.dimensionWidget.set_editMin4(lowerBound) self.dimensionWidget.set_editMax4(upperBound) def instrumentUpdateEvent(self): if self.masterDict['instrument'] == 'WAND\u00B2': self.instrumentWAND = True # change the ui accordingly self.dimensionWidget.toggleDeltaE(False) self.instrumentWidget.setLabelEi('Input Wavelength') self.instrumentWidget.setEiVal(str(1.488)) self.instrumentWidget.setGoniometerNames(['s1', 'sgl', 'sgu']) self.instrumentWidget.setGoniometerDirections( ['0,1,0', '1,0,0', '0,0,1']) self.instrumentWidget.setGoniometerRotationSense([1, -1, -1]) self.instrumentWidget.updateAll() self.eiWavelengthUpdateEvent() else: if self.instrumentWAND: self.instrumentWAND = False self.dimensionWidget.toggleDeltaE(True) self.instrumentWidget.setLabelEi('Incident Energy') self.instrumentWidget.setEiVal(str(10.0)) self.instrumentWidget.setGoniometerNames(['psi', 'gl', 'gs']) self.instrumentWidget.setGoniometerDirections( ['0,1,0', '0,0,1', '1,0,0']) self.instrumentWidget.setGoniometerRotationSense([1, 1, 1]) self.instrumentWidget.updateAll() @QtCore.Slot(dict) def updateParams(self, d): if self.sender() is self.instrumentWidget: self.updatedInstrument = True if 'dimBasis' in d and 'dimBasis' in self.masterDict and d[ 'dimBasis'] != self.masterDict['dimBasis']: self.needToClear = True if 'dimIndex' in d and 'dimIndex' in self.masterDict and d[ 'dimIndex'] != self.masterDict['dimIndex']: self.needToClear = True self.masterDict.update(copy.deepcopy(d)) def help(self): show_interface_help(self.mantidplot_name, self.assistant_process, area='direct') def closeEvent(self, event): self.assistant_process.close() self.assistant_process.waitForFinished() event.accept() def _create_goniometer_workspaces(self, gonioAxis0values, gonioAxis1values, gonioAxis2values, progressDialog): groupingStrings = [] i = 0 for g0 in gonioAxis0values: for g1 in gonioAxis1values: for g2 in gonioAxis2values: name = "__temp_instrument" + str(i) i += 1 progressDialog.setValue(i) progressDialog.setLabelText( "Creating workspace %d of %d..." % (i, self.iterations)) QtWidgets.qApp.processEvents() if progressDialog.wasCanceled(): self.progress_canceled = True progressDialog.close() return None groupingStrings.append(name) mantid.simpleapi.CloneWorkspace("__temp_instrument", OutputWorkspace=name) mantid.simpleapi.SetGoniometer( Workspace=name, Axis0=str(g0) + "," + self.masterDict['gonioDirs'][0] + "," + str(self.masterDict['gonioSenses'][0]), Axis1=str(g1) + "," + self.masterDict['gonioDirs'][1] + "," + str(self.masterDict['gonioSenses'][1]), Axis2=str(g2) + "," + self.masterDict['gonioDirs'][2] + "," + str(self.masterDict['gonioSenses'][2])) return groupingStrings # pylint: disable=too-many-locals def updateFigure(self): # noqa: C901 # pylint: disable=too-many-branches if self.updatedInstrument or self.progress_canceled: self.progress_canceled = False # get goniometer settings first gonioAxis0values = numpy.arange( self.masterDict['gonioMinvals'][0], self.masterDict['gonioMaxvals'][0] + 0.1 * self.masterDict['gonioSteps'][0], self.masterDict['gonioSteps'][0]) gonioAxis1values = numpy.arange( self.masterDict['gonioMinvals'][1], self.masterDict['gonioMaxvals'][1] + 0.1 * self.masterDict['gonioSteps'][1], self.masterDict['gonioSteps'][1]) gonioAxis2values = numpy.arange( self.masterDict['gonioMinvals'][2], self.masterDict['gonioMaxvals'][2] + 0.1 * self.masterDict['gonioSteps'][2], self.masterDict['gonioSteps'][2]) self.iterations = len(gonioAxis0values) * len( gonioAxis1values) * len(gonioAxis2values) if self.iterations > 10: reply = QtWidgets.QMessageBox.warning( self, 'Goniometer', "More than 10 goniometer settings. This might be long.\n" "Are you sure you want to proceed?", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No) if reply == QtWidgets.QMessageBox.No: return if self.wg is not None: mantid.simpleapi.DeleteWorkspace(self.wg) instrumentName = self.masterDict['instrument'] if instrumentName == 'WAND\u00B2': instrumentName = 'WAND' mantid.simpleapi.LoadEmptyInstrument( mantid.api.ExperimentInfo.getInstrumentFilename( instrumentName), OutputWorkspace="__temp_instrument") if self.masterDict['instrument'] == 'HYSPEC': mantid.simpleapi.AddSampleLog(Workspace="__temp_instrument", LogName='msd', LogText='1798.5', LogType='Number Series') mantid.simpleapi.AddSampleLog(Workspace="__temp_instrument", LogName='s2', LogText=str( self.masterDict['S2']), LogType='Number Series') mantid.simpleapi.LoadInstrument(Workspace="__temp_instrument", RewriteSpectraMap=True, InstrumentName="HYSPEC") elif self.masterDict['instrument'] == 'EXED': mantid.simpleapi.RotateInstrumentComponent( Workspace="__temp_instrument", ComponentName='Tank', Y=1, Angle=str(self.masterDict['S2']), RelativeRotation=False) elif instrumentName == 'WAND': mantid.simpleapi.AddSampleLog(Workspace="__temp_instrument", LogName='HB2C:Mot:s2.RBV', LogText=str( self.masterDict['S2']), LogType='Number Series') mantid.simpleapi.AddSampleLog(Workspace="__temp_instrument", LogName='HB2C:Mot:detz.RBV', LogText=str( self.masterDict['DetZ']), LogType='Number Series') mantid.simpleapi.LoadInstrument(Workspace="__temp_instrument", RewriteSpectraMap=True, InstrumentName="WAND") # masking if 'maskFilename' in self.masterDict and len( self.masterDict['maskFilename'].strip()) > 0: try: __maskWS = mantid.simpleapi.Load( self.masterDict['maskFilename']) mantid.simpleapi.MaskDetectors( Workspace="__temp_instrument", MaskedWorkspace=__maskWS) except (ValueError, RuntimeError) as e: reply = QtWidgets.QMessageBox.critical( self, 'Error', "The following error has occurred in loading the mask:\n" + str(e) + "\nDo you want to continue without mask?", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No) if reply == QtWidgets.QMessageBox.No: return if self.masterDict['makeFast']: sp = list( range( mantid.mtd["__temp_instrument"].getNumberHistograms())) tomask = sp[1::4] + sp[2::4] + sp[3::4] mantid.simpleapi.MaskDetectors("__temp_instrument", SpectraList=tomask) progressDialog = QtWidgets.QProgressDialog(self) progressDialog.setMinimumDuration(0) progressDialog.setCancelButtonText("&Cancel") progressDialog.setRange(0, self.iterations) progressDialog.setWindowTitle("DGSPlanner progress") groupingStrings = self._create_goniometer_workspaces( gonioAxis0values, gonioAxis1values, gonioAxis2values, progressDialog) if groupingStrings is None: return progressDialog.close() mantid.simpleapi.DeleteWorkspace("__temp_instrument") self.wg = mantid.simpleapi.GroupWorkspaces( groupingStrings, OutputWorkspace="__temp_instrument") self.updatedInstrument = False # set the UB if self.updatedOL or not self.wg[0].sample().hasOrientedLattice(): mantid.simpleapi.SetUB(self.wg, UB=self.ol.getUB()) self.updatedOL = False # calculate coverage dimensions = ['Q1', 'Q2', 'Q3', 'DeltaE'] progressDialog = QtWidgets.QProgressDialog(self) progressDialog.setMinimumDuration(0) progressDialog.setCancelButtonText("&Cancel") progressDialog.setRange(0, self.iterations) progressDialog.setWindowTitle("DGSPlanner progress") if self.masterDict['instrument'] == 'WAND\u00B2': ei = UnitConversion.run('Wavelength', 'Energy', self.masterDict['Ei'], 0, 0, 0, Elastic, 0) else: ei = self.masterDict['Ei'] for i in range(self.iterations): progressDialog.setValue(i) progressDialog.setLabelText("Calculating orientation %d of %d..." % (i, self.iterations)) QtWidgets.qApp.processEvents() if progressDialog.wasCanceled(): self.progress_canceled = True progressDialog.close() return __mdws = mantid.simpleapi.CalculateCoverageDGS( self.wg[i], Q1Basis=self.masterDict['dimBasis'][0], Q2Basis=self.masterDict['dimBasis'][1], Q3Basis=self.masterDict['dimBasis'][2], IncidentEnergy=ei, Dimension1=dimensions[self.masterDict['dimIndex'][0]], Dimension1Min=float2Input(self.masterDict['dimMin'][0]), Dimension1Max=float2Input(self.masterDict['dimMax'][0]), Dimension1Step=float2Input(self.masterDict['dimStep'][0]), Dimension2=dimensions[self.masterDict['dimIndex'][1]], Dimension2Min=float2Input(self.masterDict['dimMin'][1]), Dimension2Max=float2Input(self.masterDict['dimMax'][1]), Dimension2Step=float2Input(self.masterDict['dimStep'][1]), Dimension3=dimensions[self.masterDict['dimIndex'][2]], Dimension3Min=float2Input(self.masterDict['dimMin'][2]), Dimension3Max=float2Input(self.masterDict['dimMax'][2]), Dimension4=dimensions[self.masterDict['dimIndex'][3]], Dimension4Min=float2Input(self.masterDict['dimMin'][3]), Dimension4Max=float2Input(self.masterDict['dimMax'][3])) if i == 0: intensity = __mdws.getSignalArray( )[:, :, 0, 0] * 1. # to make it writeable else: if self.colorButton.isChecked(): tempintensity = __mdws.getSignalArray()[:, :, 0, 0] intensity[numpy.where(tempintensity > 0)] = i + 1. else: tempintensity = __mdws.getSignalArray()[:, :, 0, 0] intensity[numpy.where(tempintensity > 0)] = 1. progressDialog.close() x = numpy.linspace( __mdws.getDimension(0).getMinimum(), __mdws.getDimension(0).getMaximum(), intensity.shape[0]) y = numpy.linspace( __mdws.getDimension(1).getMinimum(), __mdws.getDimension(1).getMaximum(), intensity.shape[1]) Y, X = numpy.meshgrid(y, x) xx, yy = self.tr(X, Y) Z = numpy.ma.masked_array(intensity, intensity == 0) Z = Z[:-1, :-1] # plotting if self.sender() is self.plotButton or self.needToClear: self.figure.clear() self.trajfig.clear() self.figure.add_subplot(self.trajfig) self.needToClear = False self.trajfig.pcolorfast(xx, yy, Z) if self.aspectButton.isChecked(): self.trajfig.set_aspect(1.) else: self.trajfig.set_aspect('auto') self.trajfig.set_xlabel(self.masterDict['dimNames'][0]) self.trajfig.set_ylabel(self.masterDict['dimNames'][1]) self.trajfig.grid(True) self.canvas.draw() mantid.simpleapi.DeleteWorkspace(__mdws) def save(self): fileName = QtWidgets.QFileDialog.getSaveFileName( self, 'Save Plot', self.saveDir, '*.png') if isinstance(fileName, tuple): fileName = fileName[0] if not fileName: return data = "Instrument " + self.masterDict['instrument'] + '\n' if self.masterDict['instrument'] == 'HYSPEC': data += "S2 = " + str(self.masterDict['S2']) + '\n' data += "Ei = " + str(self.masterDict['Ei']) + ' meV\n' data += "Goniometer values:\n" gonioAxis0values = numpy.arange( self.masterDict['gonioMinvals'][0], self.masterDict['gonioMaxvals'][0] + 0.1 * self.masterDict['gonioSteps'][0], self.masterDict['gonioSteps'][0]) gonioAxis1values = numpy.arange( self.masterDict['gonioMinvals'][1], self.masterDict['gonioMaxvals'][1] + 0.1 * self.masterDict['gonioSteps'][1], self.masterDict['gonioSteps'][1]) gonioAxis2values = numpy.arange( self.masterDict['gonioMinvals'][2], self.masterDict['gonioMaxvals'][2] + 0.1 * self.masterDict['gonioSteps'][2], self.masterDict['gonioSteps'][2]) for g0 in gonioAxis0values: for g1 in gonioAxis1values: for g2 in gonioAxis2values: data += " " + self.masterDict['gonioLabels'][ 0] + " = " + str(g0) data += " " + self.masterDict['gonioLabels'][ 1] + " = " + str(g1) data += " " + self.masterDict['gonioLabels'][ 2] + " = " + str(g2) + '\n' data += "Lattice parameters:\n" data += " a = " + str(self.ol.a()) + " b = " + str( self.ol.b()) + " c = " + str(self.ol.c()) + '\n' data += " alpha = " + str(self.ol.alpha()) + " beta = " + str( self.ol.beta()) + " gamma = " + str(self.ol.gamma()) + '\n' data += "Orientation vectors:\n" data += " u = " + str(self.ol.getuVector()) + '\n' data += " v = " + str(self.ol.getvVector()) + '\n' data += "Integrated " + self.masterDict['dimNames'][2] + " between " + \ str(self.masterDict['dimMin'][2]) + " and " + str(self.masterDict['dimMax'][2]) + '\n' data += "Integrated " + self.masterDict['dimNames'][3] + " between " + \ str(self.masterDict['dimMin'][3]) + " and " + str(self.masterDict['dimMax'][3]) + '\n' info = self.figure.text(0.2, 0, data, verticalalignment='top') self.figure.savefig(fileName, bbox_inches='tight', additional_artists=info) self.saveDir = os.path.dirname(fileName) def tr(self, x, y): x, y = numpy.asarray(x), numpy.asarray(y) # one of the axes is energy if self.masterDict['dimIndex'][0] == 3 or self.masterDict['dimIndex'][ 1] == 3: return x, y else: h1, k1, l1 = (float(temp) for temp in self.masterDict['dimBasis'][ self.masterDict['dimIndex'][0]].split(',')) h2, k2, l2 = (float(temp) for temp in self.masterDict['dimBasis'][ self.masterDict['dimIndex'][1]].split(',')) angle = numpy.radians(self.ol.recAngle(h1, k1, l1, h2, k2, l2)) return 1. * x + numpy.cos(angle) * y, numpy.sin(angle) * y def inv_tr(self, x, y): x, y = numpy.asarray(x), numpy.asarray(y) # one of the axes is energy if self.masterDict['dimIndex'][0] == 3 or self.masterDict['dimIndex'][ 1] == 3: return x, y else: h1, k1, l1 = (float(temp) for temp in self.masterDict['dimBasis'][ self.masterDict['dimIndex'][0]].split(',')) h2, k2, l2 = (float(temp) for temp in self.masterDict['dimBasis'][ self.masterDict['dimIndex'][1]].split(',')) angle = numpy.radians(self.ol.recAngle(h1, k1, l1, h2, k2, l2)) return 1. * x - y / numpy.tan(angle), y / numpy.sin(angle)
from mpl_toolkits.axisartist import Subplot from mpl_toolkits.axisartist.grid_helper_curvelinear import \ GridHelperCurveLinear def tr(x, y): # source (data) to target (rectilinear plot) coordinates x, y = numpy.asarray(x), numpy.asarray(y) return x + 0.2 * y, y - x def inv_tr(x, y): x, y = numpy.asarray(x), numpy.asarray(y) return x - 0.2 * y, y + x grid_helper = GridHelperCurveLinear((tr, inv_tr)) ax6 = Subplot(fig, nrow, ncol, 6, grid_helper=grid_helper) fig.add_subplot(ax6) ax6.set_title('non-ortho axes') xx, yy = tr([3, 6], [5.0, 10.]) ax6.plot(xx, yy) ax6.set_aspect(1.) ax6.set_xlim(0, 10.) ax6.set_ylim(0, 10.) ax6.axis["t"] = ax6.new_floating_axis(0, 3.) ax6.axis["t2"] = ax6.new_floating_axis(1, 7.) ax6.grid(True) plt.show()
class DGSPlannerGUI(QtGui.QWidget): def __init__(self,ol=None,parent=None): # pylint: disable=unused-argument,super-on-old-class super(DGSPlannerGUI,self).__init__(parent) #OrientedLattice if ValidateOL(ol): self.ol=ol else: self.ol=mantid.geometry.OrientedLattice() self.masterDict=dict() #holds info about instrument and ranges self.updatedInstrument=False self.updatedOL=False self.wg=None #workspace group self.instrumentWidget=InstrumentSetupWidget.InstrumentSetupWidget(self) self.setLayout(QtGui.QHBoxLayout()) controlLayout=QtGui.QVBoxLayout() controlLayout.addWidget(self.instrumentWidget) self.ublayout=QtGui.QHBoxLayout() self.classic=ClassicUBInputWidget.ClassicUBInputWidget(self.ol) self.ublayout.addWidget(self.classic,alignment=QtCore.Qt.AlignTop,stretch=1) self.matrix=MatrixUBInputWidget.MatrixUBInputWidget(self.ol) self.ublayout.addWidget(self.matrix,alignment=QtCore.Qt.AlignTop,stretch=1) controlLayout.addLayout(self.ublayout) self.dimensionWidget=DimensionSelectorWidget.DimensionSelectorWidget(self) controlLayout.addWidget(self.dimensionWidget) plotControlLayout=QtGui.QGridLayout() self.plotButton=QtGui.QPushButton("Plot",self) self.oplotButton=QtGui.QPushButton("Overplot",self) self.helpButton=QtGui.QPushButton("?",self) self.colorLabel=QtGui.QLabel('Color by angle',self) self.colorButton=QtGui.QCheckBox(self) self.colorButton.toggle() self.aspectLabel=QtGui.QLabel('Aspect ratio 1:1',self) self.aspectButton=QtGui.QCheckBox(self) self.saveButton=QtGui.QPushButton("Save Figure",self) plotControlLayout.addWidget(self.plotButton,0,0) plotControlLayout.addWidget(self.oplotButton,0,1) plotControlLayout.addWidget(self.colorLabel,0,2,QtCore.Qt.AlignRight) plotControlLayout.addWidget(self.colorButton,0,3) plotControlLayout.addWidget(self.aspectLabel,0,4,QtCore.Qt.AlignRight) plotControlLayout.addWidget(self.aspectButton,0,5) plotControlLayout.addWidget(self.helpButton,0,6) plotControlLayout.addWidget(self.saveButton,0,7) controlLayout.addLayout(plotControlLayout) self.layout().addLayout(controlLayout) #figure self.figure=Figure() self.figure.patch.set_facecolor('white') self.canvas=FigureCanvas(self.figure) self.grid_helper = GridHelperCurveLinear((self.tr, self.inv_tr)) self.trajfig = Subplot(self.figure, 1, 1, 1, grid_helper=self.grid_helper) self.trajfig.hold(True) self.figure.add_subplot(self.trajfig) self.layout().addWidget(self.canvas) self.needToClear=False self.saveDir='' #connections self.matrix.UBmodel.changed.connect(self.updateUB) self.matrix.UBmodel.changed.connect(self.classic.updateOL) self.classic.changed.connect(self.matrix.UBmodel.updateOL) self.classic.changed.connect(self.updateUB) self.instrumentWidget.changed.connect(self.updateParams) self.dimensionWidget.changed.connect(self.updateParams) self.plotButton.clicked.connect(self.updateFigure) self.oplotButton.clicked.connect(self.updateFigure) self.helpButton.clicked.connect(self.help) self.saveButton.clicked.connect(self.save) #force an update of values self.instrumentWidget.updateAll() self.dimensionWidget.updateChanges() #help self.assistantProcess = QtCore.QProcess(self) # pylint: disable=protected-access self.collectionFile=os.path.join(mantid._bindir,'../docs/qthelp/MantidProject.qhc') version = ".".join(mantid.__version__.split(".")[:2]) self.qtUrl='qthelp://org.sphinx.mantidproject.'+version+'/doc/interfaces/DGSPlanner.html' self.externalUrl='http://docs.mantidproject.org/nightly/interfaces/DGSPlanner.html' #control for cancel button self.iterations=0 self.progress_canceled=False #register startup mantid.UsageService.registerFeatureUsage("Interface","DGSPlanner",False) @QtCore.pyqtSlot(mantid.geometry.OrientedLattice) def updateUB(self,ol): self.ol=ol self.updatedOL=True self.trajfig.clear() @QtCore.pyqtSlot(dict) def updateParams(self,d): if self.sender() is self.instrumentWidget: self.updatedInstrument=True if 'dimBasis' in d and 'dimBasis' in self.masterDict and d['dimBasis']!=self.masterDict['dimBasis']: self.needToClear=True if 'dimIndex' in d and 'dimIndex' in self.masterDict and d['dimIndex']!=self.masterDict['dimIndex']: self.needToClear=True self.masterDict.update(copy.deepcopy(d)) def help(self): try: import pymantidplot pymantidplot.proxies.showCustomInterfaceHelp('DGSPlanner') except ImportError: self.assistantProcess.close() self.assistantProcess.waitForFinished() helpapp = QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.BinariesPath) + QtCore.QDir.separator() helpapp += 'assistant' args = ['-enableRemoteControl', '-collectionFile',self.collectionFile,'-showUrl',self.qtUrl] if os.path.isfile(helpapp) and os.path.isfile(self.collectionFile): self.assistantProcess.close() self.assistantProcess.waitForFinished() self.assistantProcess.start(helpapp, args) else: mqt.MantidQt.API.MantidDesktopServices.openUrl(QtCore.QUrl(self.externalUrl)) def closeEvent(self,event): self.assistantProcess.close() self.assistantProcess.waitForFinished() event.accept() # pylint: disable=too-many-locals def updateFigure(self): # pylint: disable=too-many-branches if self.updatedInstrument or self.progress_canceled: self.progress_canceled=False #get goniometer settings first gonioAxis0values=numpy.arange(self.masterDict['gonioMinvals'][0],self.masterDict['gonioMaxvals'][0] +0.1*self.masterDict['gonioSteps'][0],self.masterDict['gonioSteps'][0]) gonioAxis1values=numpy.arange(self.masterDict['gonioMinvals'][1],self.masterDict['gonioMaxvals'][1] +0.1*self.masterDict['gonioSteps'][1],self.masterDict['gonioSteps'][1]) gonioAxis2values=numpy.arange(self.masterDict['gonioMinvals'][2],self.masterDict['gonioMaxvals'][2] +0.1*self.masterDict['gonioSteps'][2],self.masterDict['gonioSteps'][2]) self.iterations=len(gonioAxis0values)*len(gonioAxis1values)*len(gonioAxis2values) if self.iterations>10: reply = QtGui.QMessageBox.warning(self, 'Goniometer',"More than 10 goniometer settings. This might be long.\n" "Are you sure you want to proceed?", QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No) if reply==QtGui.QMessageBox.No: return if self.wg is not None: mantid.simpleapi.DeleteWorkspace(self.wg) mantid.simpleapi.LoadEmptyInstrument(mantid.api.ExperimentInfo.getInstrumentFilename(self.masterDict['instrument']), OutputWorkspace="__temp_instrument") if self.masterDict['instrument']=='HYSPEC': mantid.simpleapi.AddSampleLog(Workspace="__temp_instrument",LogName='msd',LogText='1798.5',LogType='Number Series') mantid.simpleapi.AddSampleLog(Workspace="__temp_instrument",LogName='s2', LogText=str(self.masterDict['S2']),LogType='Number Series') mantid.simpleapi.LoadInstrument(Workspace="__temp_instrument", RewriteSpectraMap=True, InstrumentName="HYSPEC") #masking if 'maskFilename' in self.masterDict and len(self.masterDict['maskFilename'].strip())>0: try: __maskWS=mantid.simpleapi.Load(self.masterDict['maskFilename']) mantid.simpleapi.MaskDetectors(Workspace="__temp_instrument",MaskedWorkspace=__maskWS) except (ValueError,RuntimeError) as e: reply = QtGui.QMessageBox.critical(self, 'Error',"The following error has occured in loading the mask:\n"+ str(e)+"\nDo you want to continue without mask?", QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No) if reply==QtGui.QMessageBox.No: return if self.masterDict['makeFast']: sp=range(mantid.mtd["__temp_instrument"].getNumberHistograms()) tomask=sp[::4]+sp[1::4]+sp[2::4] mantid.simpleapi.MaskDetectors("__temp_instrument",SpectraList=tomask) i=0 groupingStrings=[] progressDialog = QtGui.QProgressDialog(self) progressDialog.setMinimumDuration(0) progressDialog.setCancelButtonText("&Cancel") progressDialog.setRange(0, self.iterations) progressDialog.setWindowTitle("DGSPlanner progress") for g0 in gonioAxis0values: for g1 in gonioAxis1values: for g2 in gonioAxis2values: name="__temp_instrument"+str(i) i+=1 progressDialog.setValue(i) progressDialog.setLabelText("Creating workspace %d of %d..." % (i, self.iterations)) QtGui.qApp.processEvents() if progressDialog.wasCanceled(): self.progress_canceled=True progressDialog.close() return groupingStrings.append(name) mantid.simpleapi.CloneWorkspace("__temp_instrument",OutputWorkspace=name) mantid.simpleapi.SetGoniometer(Workspace=name, Axis0=str(g0)+","+self.masterDict['gonioDirs'][0]+ ","+str(self.masterDict['gonioSenses'][0]), Axis1=str(g1)+","+self.masterDict['gonioDirs'][1]+ ","+str(self.masterDict['gonioSenses'][1]), Axis2=str(g2)+","+self.masterDict['gonioDirs'][2]+ ","+str(self.masterDict['gonioSenses'][2])) progressDialog.close() mantid.simpleapi.DeleteWorkspace("__temp_instrument") self.wg=mantid.simpleapi.GroupWorkspaces(groupingStrings,OutputWorkspace="__temp_instrument") self.updatedInstrument=False #set the UB if self.updatedOL or not self.wg[0].sample().hasOrientedLattice(): mantid.simpleapi.SetUB(self.wg,UB=self.ol.getUB()) self.updatedOL=False #calculate coverage dimensions=['Q1','Q2','Q3','DeltaE'] progressDialog = QtGui.QProgressDialog(self) progressDialog.setMinimumDuration(0) progressDialog.setCancelButtonText("&Cancel") progressDialog.setRange(0, self.iterations) progressDialog.setWindowTitle("DGSPlanner progress") for i in range(self.iterations): progressDialog.setValue(i) progressDialog.setLabelText("Calculating orientation %d of %d..." % (i, self.iterations)) QtGui.qApp.processEvents() if progressDialog.wasCanceled(): self.progress_canceled=True progressDialog.close() return __mdws=mantid.simpleapi.CalculateCoverageDGS(self.wg[i], Q1Basis=self.masterDict['dimBasis'][0], Q2Basis=self.masterDict['dimBasis'][1], Q3Basis=self.masterDict['dimBasis'][2], IncidentEnergy=self.masterDict['Ei'], Dimension1=dimensions[self.masterDict['dimIndex'][0]], Dimension1Min=float2Input(self.masterDict['dimMin'][0]), Dimension1Max=float2Input(self.masterDict['dimMax'][0]), Dimension1Step=float2Input(self.masterDict['dimStep'][0]), Dimension2=dimensions[self.masterDict['dimIndex'][1]], Dimension2Min=float2Input(self.masterDict['dimMin'][1]), Dimension2Max=float2Input(self.masterDict['dimMax'][1]), Dimension2Step=float2Input(self.masterDict['dimStep'][1]), Dimension3=dimensions[self.masterDict['dimIndex'][2]], Dimension3Min=float2Input(self.masterDict['dimMin'][2]), Dimension3Max=float2Input(self.masterDict['dimMax'][2]), Dimension4=dimensions[self.masterDict['dimIndex'][3]], Dimension4Min=float2Input(self.masterDict['dimMin'][3]), Dimension4Max=float2Input(self.masterDict['dimMax'][3])) if i==0: intensity=__mdws.getSignalArray()[:,:,0,0]*1. #to make it writeable else: if self.colorButton.isChecked(): tempintensity= __mdws.getSignalArray()[:,:,0,0] intensity[numpy.where( tempintensity>0)]=i+1. else: tempintensity= __mdws.getSignalArray()[:,:,0,0] intensity[numpy.where( tempintensity>0)]=1. progressDialog.close() x = numpy.linspace(__mdws.getDimension(0).getMinimum(), __mdws.getDimension(0).getMaximum(),intensity.shape[0] ) y = numpy.linspace(__mdws.getDimension(1).getMinimum(), __mdws.getDimension(1).getMaximum(),intensity.shape[1] ) Y,X = numpy.meshgrid(y,x) xx, yy = self.tr(X, Y) Z=numpy.ma.masked_array(intensity,intensity==0) Z = Z[:-1, :-1] #plotting if self.sender() is self.plotButton or self.needToClear: self.figure.clear() self.trajfig.clear() self.figure.add_subplot(self.trajfig) self.needToClear=False self.trajfig.pcolorfast(xx,yy,Z) if self.aspectButton.isChecked(): self.trajfig.set_aspect(1.) else: self.trajfig.set_aspect('auto') self.trajfig.set_xlabel(self.masterDict['dimNames'][0]) self.trajfig.set_ylabel(self.masterDict['dimNames'][1]) self.trajfig.grid(True) self.canvas.draw() mantid.simpleapi.DeleteWorkspace(__mdws) def save(self): fileName = str(QtGui.QFileDialog.getSaveFileName(self, 'Save Plot', self.saveDir,'*.png')) data = "Instrument "+self.masterDict['instrument']+'\n' if self.masterDict['instrument']=='HYSPEC': data+= "S2 = "+str(self.masterDict['S2'])+'\n' data+= "Ei = "+str(self.masterDict['Ei'])+' meV\n' data+= "Goniometer values:\n" gonioAxis0values=numpy.arange(self.masterDict['gonioMinvals'][0],self.masterDict['gonioMaxvals'][0] +0.1*self.masterDict['gonioSteps'][0],self.masterDict['gonioSteps'][0]) gonioAxis1values=numpy.arange(self.masterDict['gonioMinvals'][1],self.masterDict['gonioMaxvals'][1] +0.1*self.masterDict['gonioSteps'][1],self.masterDict['gonioSteps'][1]) gonioAxis2values=numpy.arange(self.masterDict['gonioMinvals'][2],self.masterDict['gonioMaxvals'][2] +0.1*self.masterDict['gonioSteps'][2],self.masterDict['gonioSteps'][2]) for g0 in gonioAxis0values: for g1 in gonioAxis1values: for g2 in gonioAxis2values: data+=" "+self.masterDict['gonioLabels'][0]+" = "+str(g0) data+=" "+self.masterDict['gonioLabels'][1]+" = "+str(g1) data+=" "+self.masterDict['gonioLabels'][2]+" = "+str(g2)+'\n' data+= "Lattice parameters:\n" data+=" a = "+str(self.ol.a())+" b = "+str(self.ol.b())+" c = "+str(self.ol.c())+'\n' data+=" alpha = "+str(self.ol.alpha())+" beta = "+str(self.ol.beta())+" gamma = "+str(self.ol.gamma())+'\n' data+= "Orientation vectors:\n" data+=" u = "+str(self.ol.getuVector())+'\n' data+=" v = "+str(self.ol.getvVector())+'\n' data+="Integrated "+self.masterDict['dimNames'][2]+" between "+\ str(self.masterDict['dimMin'][2])+" and "+str(self.masterDict['dimMax'][2])+'\n' data+="Integrated "+self.masterDict['dimNames'][3]+" between "+\ str(self.masterDict['dimMin'][3])+" and "+str(self.masterDict['dimMax'][3])+'\n' info=self.figure.text(0.2,0,data,verticalalignment='top') self.figure.savefig(fileName,bbox_inches='tight',additional_artists=info) self.saveDir=os.path.dirname(fileName) def tr(self,x, y): x, y = numpy.asarray(x), numpy.asarray(y) #one of the axes is energy if self.masterDict['dimIndex'][0]==3 or self.masterDict['dimIndex'][1]==3: return x,y else: h1,k1,l1=(float(temp) for temp in self.masterDict['dimBasis'][self.masterDict['dimIndex'][0]].split(',')) h2,k2,l2=(float(temp) for temp in self.masterDict['dimBasis'][self.masterDict['dimIndex'][1]].split(',')) angle=numpy.radians(self.ol.recAngle(h1,k1,l1,h2,k2,l2)) return 1.*x+numpy.cos(angle)*y, numpy.sin(angle)*y def inv_tr(self,x,y): x, y = numpy.asarray(x), numpy.asarray(y) #one of the axes is energy if self.masterDict['dimIndex'][0]==3 or self.masterDict['dimIndex'][1]==3: return x,y else: h1,k1,l1=(float(temp) for temp in self.masterDict['dimBasis'][self.masterDict['dimIndex'][0]].split(',')) h2,k2,l2=(float(temp) for temp in self.masterDict['dimBasis'][self.masterDict['dimIndex'][1]].split(',')) angle=numpy.radians(self.ol.recAngle(h1,k1,l1,h2,k2,l2)) return 1.*x-y/numpy.tan(angle), y/numpy.sin(angle)
label=band, color=v, peak=False, bins=bins, density=True) ax2.yaxis.set_major_locator(MaxNLocator(6, prune='both')) ax2.axis['left'].major_ticklabels.set_visible(True) ax2.axis['right'].major_ticklabels.set_visible(False) ax2.axis['left'].label.set_visible(True) #ax2.axis['right'].label.set_text(r'Number of Tiles') ax2.axis['left'].label.set_text(r'PDF') ax2.axis['bottom'].label.set_visible(True) ax2.axis['bottom'].label.set_text( r'Surface Brightness Limit (mag arcsec$^{-2}$)') if band == 'z': ax2.set_aspect(1.5) elif band == 'Y': ax2.set_aspect(1.2) else: ax2.set_aspect(0.8) #forceAspect(ax2,aspect=1) #plt.suptitle(f'{band}-band ({size}" x {size}")',y=0.92) plt.legend(loc='upper left') outfile = f'sbcontrast_{band}_s{size:d}_v2.pdf' print(f" {outfile}") plt.savefig(outfile, bbox_inches='tight') #outfile = outfile.replace('.pdf','.png')
class Tephigram: """ Generate a tephigram of one or more pressure and temperature data sets. """ def __init__( self, figure=None, isotherm_locator=None, dry_adiabat_locator=None, anchor=None, ): """ Initialise the tephigram transformation and plot axes. Kwargs: * figure: An existing :class:`matplotlib.figure.Figure` instance for the tephigram plot. If a figure is not provided, a new figure will be created by default. * isotherm_locator: A :class:`tephi.Locator` instance or a numeric step size for the isotherm lines. * dry_adiabat_locator: A :class:`tephi.Locator` instance or a numeric step size for the dry adiabat lines. * anchor: A sequence of two pressure, temperature pairs specifying the extent of the tephigram plot in terms of the bottom left hand corner and the top right hand corner. Pressure data points must be in units of mb or hPa, and temperature data points must be in units of degC. For example: .. plot:: :include-source: import matplotlib.pyplot as plt from numpy import column_stack import os.path import tephi from tephi import Tephigram dew_point = os.path.join(tephi.DATA_DIR, 'dews.txt') dry_bulb = os.path.join(tephi.DATA_DIR, 'temps.txt') dew_data, temp_data = tephi.loadtxt(dew_point, dry_bulb) dews = column_stack((dew_data.pressure, dew_data.temperature)) temps = column_stack((temp_data.pressure, temp_data.temperature)) tpg = Tephigram() tpg.plot(dews, label='Dew-point', color='blue', linewidth=2) tpg.plot(temps, label='Dry-bulb', color='red', linewidth=2) plt.show() """ if not figure: # Create a default figure. self.figure = plt.figure(0, figsize=(9, 9)) else: self.figure = figure # Configure the locators. if isotherm_locator and not isinstance(isotherm_locator, Locator): if not isinstance(isotherm_locator, numbers.Number): raise ValueError("Invalid isotherm locator") locator_isotherm = Locator(isotherm_locator) else: locator_isotherm = isotherm_locator if dry_adiabat_locator and not isinstance(dry_adiabat_locator, Locator): if not isinstance(dry_adiabat_locator, numbers.Number): raise ValueError("Invalid dry adiabat locator") locator_theta = Locator(dry_adiabat_locator) else: locator_theta = dry_adiabat_locator # Define the tephigram coordinate-system transformation. self.tephi_transform = transforms.TephiTransform() ghelper = GridHelperCurveLinear( self.tephi_transform, tick_formatter1=_FormatterIsotherm(), grid_locator1=locator_isotherm, tick_formatter2=_FormatterTheta(), grid_locator2=locator_theta, ) self.axes = Subplot(self.figure, 1, 1, 1, grid_helper=ghelper) self.transform = self.tephi_transform + self.axes.transData self.axes.axis["isotherm"] = self.axes.new_floating_axis(1, 0) self.axes.axis["theta"] = self.axes.new_floating_axis(0, 0) self.axes.axis["left"].get_helper().nth_coord_ticks = 0 self.axes.axis["left"].toggle(all=True) self.axes.axis["bottom"].get_helper().nth_coord_ticks = 1 self.axes.axis["bottom"].toggle(all=True) self.axes.axis["top"].get_helper().nth_coord_ticks = 0 self.axes.axis["top"].toggle(all=False) self.axes.axis["right"].get_helper().nth_coord_ticks = 1 self.axes.axis["right"].toggle(all=True) self.axes.gridlines.set_linestyle("solid") self.figure.add_subplot(self.axes) # Configure default axes. axis = self.axes.axis["left"] axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va("baseline") axis.major_ticklabels.set_rotation(135) axis = self.axes.axis["right"] axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va("baseline") axis.major_ticklabels.set_rotation(-135) self.axes.axis["top"].major_ticklabels.set_fontsize(10) axis = self.axes.axis["bottom"] axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_ha("left") axis.major_ticklabels.set_va("top") axis.major_ticklabels.set_rotation(-45) # Isotherms: lines of constant temperature (degC). axis = self.axes.axis["isotherm"] axis.set_axis_direction("right") axis.set_axislabel_direction("-") axis.major_ticklabels.set_rotation(90) axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va("bottom") axis.major_ticklabels.set_color("grey") axis.major_ticklabels.set_visible(False) # turned-off # Dry adiabats: lines of constant potential temperature (degC). axis = self.axes.axis["theta"] axis.set_axis_direction("right") axis.set_axislabel_direction("+") axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va("bottom") axis.major_ticklabels.set_color("grey") axis.major_ticklabels.set_visible(False) # turned-off axis.line.set_linewidth(3) axis.line.set_linestyle("--") # Lock down the aspect ratio. self.axes.set_aspect(1.0) self.axes.grid(True) # Initialise the text formatter for the navigation status bar. self.axes.format_coord = self._status_bar # Factor in the tephigram transform. ISOBAR_TEXT["transform"] = self.transform WET_ADIABAT_TEXT["transform"] = self.transform MIXING_RATIO_TEXT["transform"] = self.transform # Create plot collections for the tephigram isopleths. func = partial( isopleths.isobar, MIN_THETA, MAX_THETA, self.axes, self.transform, ISOBAR_LINE, ) self._isobars = _PlotCollection( self.axes, ISOBAR_SPEC, MAX_PRESSURE, func, ISOBAR_TEXT, fixed=ISOBAR_FIXED, minimum=MIN_PRESSURE, ) func = partial( isopleths.wet_adiabat, MAX_PRESSURE, MIN_TEMPERATURE, self.axes, self.transform, WET_ADIABAT_LINE, ) self._wet_adiabats = _PlotCollection( self.axes, WET_ADIABAT_SPEC, MAX_WET_ADIABAT, func, WET_ADIABAT_TEXT, fixed=WET_ADIABAT_FIXED, minimum=MIN_WET_ADIABAT, xfocus=True, ) func = partial( isopleths.mixing_ratio, MIN_PRESSURE, MAX_PRESSURE, self.axes, self.transform, MIXING_RATIO_LINE, ) self._mixing_ratios = _PlotCollection( self.axes, MIXING_RATIO_SPEC, MIXING_RATIOS, func, MIXING_RATIO_TEXT, fixed=MIXING_RATIO_FIXED, ) # Initialise for the tephigram plot event handler. plt.connect("motion_notify_event", _handler) self.axes.tephigram = True self.axes.tephigram_original_delta_xlim = DEFAULT_WIDTH self.original_delta_xlim = DEFAULT_WIDTH self.axes.tephigram_transform = self.tephi_transform self.axes.tephigram_inverse = self.tephi_transform.inverted() self.axes.tephigram_isopleths = [ self._isobars, self._wet_adiabats, self._mixing_ratios, ] # The tephigram profiles. self._profiles = [] self.axes.tephigram_profiles = self._profiles # Center the plot around the anchor extent. self._anchor = anchor if self._anchor is not None: self._anchor = np.asarray(anchor) if (self._anchor.ndim != 2 or self._anchor.shape[-1] != 2 or len(self._anchor) != 2): msg = ("Invalid anchor, expecting [(bottom-left-pressure, " "bottom-left-temperature), (top-right-pressure, " "top-right-temperature)]") raise ValueError(msg) ( (bottom_pressure, bottom_temp), (top_pressure, top_temp), ) = self._anchor if (bottom_pressure - top_pressure) < 0: raise ValueError("Invalid anchor pressure range") if (bottom_temp - top_temp) < 0: raise ValueError("Invalid anchor temperature range") self._anchor = isopleths.Profile(anchor, self.axes) self._anchor.plot(visible=False) xlim, ylim = self._calculate_extents() self.axes.set_xlim(xlim) self.axes.set_ylim(ylim) def plot(self, data, **kwargs): """ Plot the environmental lapse rate profile of the pressure and temperature data points. The pressure and temperature data points are transformed into potential temperature and temperature data points before plotting. By default, the tephigram will automatically center the plot around all profiles. .. warning:: Pressure data points must be in units of mb or hPa, and temperature data points must be in units of degC. Args: * data: pressure and temperature pair data points. .. note:: All keyword arguments are passed through to :func:`matplotlib.pyplot.plot`. For example: .. plot:: :include-source: import matplotlib.pyplot as plt from tephi import Tephigram tpg = Tephigram() data = [[1006, 26.4], [924, 20.3], [900, 19.8], [850, 14.5], [800, 12.9], [755, 8.3]] profile = tpg.plot(data, color='red', linestyle='--', linewidth=2, marker='o') barbs = [(10, 45, 900), (20, 60, 850), (25, 90, 800)] profile.barbs(barbs) plt.show() For associating wind barbs with an environmental lapse rate profile, see :meth:`~tephi.isopleths.Profile.barbs`. """ profile = isopleths.Profile(data, self.axes) profile.plot(**kwargs) self._profiles.append(profile) # Center the tephigram plot around all the profiles. if self._anchor is None: xlim, ylim = self._calculate_extents(xfactor=0.25, yfactor=0.05) self.axes.set_xlim(xlim) self.axes.set_ylim(ylim) # Refresh the tephigram plot isopleths. _refresh_isopleths(self.axes) # Show the plot legend. if "label" in kwargs: font_properties = FontProperties(size="x-small") plt.legend( loc="upper left", fancybox=True, shadow=True, prop=font_properties, ) return profile def _status_bar(self, x_point, y_point): """Generate text for the interactive backend navigation status bar.""" temperature, theta = transforms.convert_xy2Tt(x_point, y_point) pressure, _ = transforms.convert_Tt2pT(temperature, theta) xlim = self.axes.get_xlim() zoom = (xlim[1] - xlim[0]) / self.original_delta_xlim msg = "T:{:.2f}, theta:{:.2f}, phi:{:.2f} (zoom:{:.3f})" text = msg.format(float(temperature), float(theta), float(pressure), zoom) return text def _calculate_extents(self, xfactor=None, yfactor=None): min_x = min_y = 1e10 max_x = max_y = -1e-10 profiles = self._profiles transform = self.tephi_transform.transform if self._anchor is not None: profiles = [self._anchor] for profile in profiles: temperature = profile.temperature.reshape(-1, 1) theta = profile.theta.reshape(-1, 1) xy_points = transform(np.concatenate((temperature, theta), axis=1)) x_points = xy_points[:, 0] y_points = xy_points[:, 1] min_x = np.min([min_x, np.min(x_points)]) min_y = np.min([min_y, np.min(y_points)]) max_x = np.max([max_x, np.max(x_points)]) max_y = np.max([max_y, np.max(y_points)]) if xfactor is not None: delta_x = max_x - min_x min_x, max_x = min_x - xfactor * delta_x, max_x + xfactor * delta_x if yfactor is not None: delta_y = max_y - min_y min_y, max_y = min_y - yfactor * delta_y, max_y + yfactor * delta_y return ([min_x, max_x], [min_y, max_y])
class Tephigram(object): """ Generate a tephigram of one or more pressure and temperature data sets. """ def __init__(self, figure=None, isotherm_locator=None, dry_adiabat_locator=None, anchor=None): """ Initialise the tephigram transformation and plot axes. Kwargs: * figure: An existing :class:`matplotlib.figure.Figure` instance for the tephigram plot. If a figure is not provided, a new figure will be created by default. * isotherm_locator: A :class:`edson.Locator` instance or a numeric step size for the isotherm lines. * dry_adiabat_locator: A :class:`edson.Locator` instance or a numeric step size for the dry adiabat lines. * anchor: A sequence of two pressure, temperature pairs specifying the extent of the tephigram plot in terms of the bottom left hand corner and the top right hand corner. Pressure data points must be in units of mb or hPa, and temperature data points must be in units of degC. For example: .. plot:: :include-source: import matplotlib.pyplot as plt import os.path import edson from edson import Tephigram dew_point = os.path.join(edson.RESOURCES_DIR, 'tephigram', 'dews.txt') dry_bulb = os.path.join(edson.RESOURCES_DIR, 'tephigram', 'temps.txt') dew_data, temp_data = edson.loadtxt(dew_point, dry_bulb) dews = zip(dew_data.pressure, dew_data.temperature) temps = zip(temp_data.pressure, temp_data.temperature) tephi = Tephigram() tephi.plot(dews, label='Dew-point', color='blue', linewidth=2, marker='s') tephi.plot(temps, label='Dry-bulb', color='red', linewidth=2, marker='o') plt.show() """ if not figure: # Create a default figure. self.figure = plt.figure(0, figsize=(9, 9)) else: self.figure = figure # Configure the locators. if isotherm_locator and not isinstance(isotherm_locator, Locator): if not isinstance(isotherm_locator, numbers.Number): raise ValueError('Invalid isotherm locator') locator_isotherm = Locator(isotherm_locator) else: locator_isotherm = isotherm_locator if dry_adiabat_locator and not isinstance(dry_adiabat_locator, Locator): if not isinstance(dry_adiabat_locator, numbers.Number): raise ValueError('Invalid dry adiabat locator') locator_theta = Locator(dry_adiabat_locator) else: locator_theta = dry_adiabat_locator # Define the tephigram coordinate-system transformation. self.tephi_transform = transforms.TephiTransform() grid_helper1 = GridHelperCurveLinear(self.tephi_transform, tick_formatter1=_FormatterIsotherm(), grid_locator1=locator_isotherm, tick_formatter2=_FormatterTheta(), grid_locator2=locator_theta) self.axes = Subplot(self.figure, 1, 1, 1, grid_helper=grid_helper1) self.transform = self.tephi_transform + self.axes.transData self.axes.axis['isotherm'] = self.axes.new_floating_axis(1, 0) self.axes.axis['theta'] = self.axes.new_floating_axis(0, 0) self.axes.axis['left'].get_helper().nth_coord_ticks = 0 self.axes.axis['left'].toggle(all=True) self.axes.axis['bottom'].get_helper().nth_coord_ticks = 1 self.axes.axis['bottom'].toggle(all=True) self.axes.axis['top'].get_helper().nth_coord_ticks = 0 self.axes.axis['top'].toggle(all=False) self.axes.axis['right'].get_helper().nth_coord_ticks = 1 self.axes.axis['right'].toggle(all=True) self.axes.gridlines.set_linestyle('solid') self.figure.add_subplot(self.axes) # Configure default axes. axis = self.axes.axis['left'] axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va('baseline') axis.major_ticklabels.set_rotation(135) axis = self.axes.axis['right'] axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va('baseline') axis.major_ticklabels.set_rotation(-135) self.axes.axis['top'].major_ticklabels.set_fontsize(10) axis = self.axes.axis['bottom'] axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_ha('left') axis.major_ticklabels.set_va('top') axis.major_ticklabels.set_rotation(-45) # Isotherms: lines of constant temperature (degC). axis = self.axes.axis['isotherm'] axis.set_axis_direction('right') axis.set_axislabel_direction('-') axis.major_ticklabels.set_rotation(90) axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va('bottom') axis.major_ticklabels.set_color('grey') axis.major_ticklabels.set_visible(False) # turned-off # Dry adiabats: lines of constant potential temperature (degC). axis = self.axes.axis['theta'] axis.set_axis_direction('right') axis.set_axislabel_direction('+') axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va('bottom') axis.major_ticklabels.set_color('grey') axis.major_ticklabels.set_visible(False) # turned-off axis.line.set_linewidth(3) axis.line.set_linestyle('--') # Lock down the aspect ratio. self.axes.set_aspect(1.) self.axes.grid(True) # Initialise the text formatter for the navigation status bar. self.axes.format_coord = self._status_bar # Factor in the tephigram transform. ISOBAR_TEXT['transform'] = self.transform WET_ADIABAT_TEXT['transform'] = self.transform MIXING_RATIO_TEXT['transform'] = self.transform # Create plot collections for the tephigram isopleths. func = partial(isopleths.isobar, MIN_THETA, MAX_THETA, self.axes, self.transform, ISOBAR_LINE) self._isobars = _PlotCollection(self.axes, ISOBAR_SPEC, MAX_PRESSURE, func, ISOBAR_TEXT, fixed=ISOBAR_FIXED, minimum=MIN_PRESSURE) func = partial(isopleths.wet_adiabat, MAX_PRESSURE, MIN_TEMPERATURE, self.axes, self.transform, WET_ADIABAT_LINE) self._wet_adiabats = _PlotCollection(self.axes, WET_ADIABAT_SPEC, MAX_WET_ADIABAT, func, WET_ADIABAT_TEXT, fixed=WET_ADIABAT_FIXED, minimum=MIN_WET_ADIABAT, xfocus=True) func = partial(isopleths.mixing_ratio, MIN_PRESSURE, MAX_PRESSURE, self.axes, self.transform, MIXING_RATIO_LINE) self._mixing_ratios = _PlotCollection(self.axes, MIXING_RATIO_SPEC, MIXING_RATIOS, func, MIXING_RATIO_TEXT, fixed=MIXING_RATIO_FIXED) # Initialise for the tephigram plot event handler. plt.connect('motion_notify_event', _handler) self.axes.tephigram = True self.axes.tephigram_original_delta_xlim = self.original_delta_xlim = DEFAULT_WIDTH self.axes.tephigram_transform = self.tephi_transform self.axes.tephigram_inverse = self.tephi_transform.inverted() self.axes.tephigram_isopleths = [self._isobars, self._wet_adiabats, self._mixing_ratios] # The tephigram profiles. self._profiles = [] self.axes.tephigram_profiles = self._profiles # Center the plot around the anchor extent. self._anchor = anchor if self._anchor is not None: self._anchor = np.asarray(anchor) if self._anchor.ndim != 2 or self._anchor.shape[-1] != 2 or \ len(self._anchor) != 2: msg = 'Invalid anchor, expecting [(bottom-left-pressure, ' \ 'bottom-left-temperature), (top-right-pressure, ' \ 'top-right-temperature)]' raise ValueError(msg) (bottom_pressure, bottom_temp), \ (top_pressure, top_temp) = self._anchor if (bottom_pressure - top_pressure) < 0: raise ValueError('Invalid anchor pressure range') if (bottom_temp - top_temp) < 0: raise ValueError('Invalid anchor temperature range') self._anchor = isopleths.Profile(anchor, self.axes) self._anchor.plot(visible=False) xlim, ylim = self._calculate_extents() self.axes.set_xlim(xlim) self.axes.set_ylim(ylim) def plot(self, data, **kwargs): """ Plot the environmental lapse rate profile of the pressure and temperature data points. The pressure and temperature data points are transformed into potential temperature and temperature data points before plotting. By default, the tephigram will automatically center the plot around all profiles. .. warning:: Pressure data points must be in units of mb or hPa, and temperature data points must be in units of degC. Args: * data: pressure and temperature pair data points. .. note:: All keyword arguments are passed through to :func:`matplotlib.pyplot.plot`. For example: .. plot:: :include-source: import matplotlib.pyplot as plt from edson import Tephigram tephi = Tephigram() data = [[1006, 26.4], [924, 20.3], [900, 19.8], [850, 14.5], [800, 12.9], [755, 8.3]] profile = tephi.plot(data, color='red', linestyle='--', linewidth=2, marker='o') barbs = [(10, 45, 900), (20, 60, 850), (25, 90, 800)] profile.barbs(barbs) plt.show() For associating wind barbs with an environmental lapse rate profile, see :meth:`~edson.isopleths.Profile.barbs`. """ profile = isopleths.Profile(data, self.axes) profile.plot(**kwargs) self._profiles.append(profile) # Center the tephigram plot around all the profiles. if self._anchor is None: xlim, ylim = self._calculate_extents(xfactor=.25, yfactor=.05) self.axes.set_xlim(xlim) self.axes.set_ylim(ylim) # Refresh the tephigram plot isopleths. _refresh_isopleths(self.axes) # Show the plot legend. if 'label' in kwargs: font_properties = FontProperties(size='x-small') plt.legend(loc='upper left', fancybox=True, shadow=True, prop=font_properties) return profile def _status_bar(self, x_point, y_point): """Generate text for the interactive backend navigation status bar.""" temperature, theta = transforms.xy_to_temperature_theta(x_point, y_point) pressure, _ = transforms.temperature_theta_to_pressure_temperature(temperature, theta) xlim = self.axes.get_xlim() zoom = (xlim[1] - xlim[0]) / self.original_delta_xlim text = "T:%.2f, theta:%.2f, phi:%.2f (zoom:%.3f)" % (float(temperature), float(theta), float(pressure), zoom) return text def _calculate_extents(self, xfactor=None, yfactor=None): min_x = min_y = 1e10 max_x = max_y = -1e-10 profiles = self._profiles if self._anchor is not None: profiles = [self._anchor] for profile in profiles: xy_points = self.tephi_transform.transform(np.concatenate((profile.temperature.reshape(-1, 1), profile.theta.reshape(-1, 1)), axis=1)) x_points = xy_points[:, 0] y_points = xy_points[:, 1] min_x, min_y = np.min([min_x, np.min(x_points)]), np.min([min_y, np.min(y_points)]) max_x, max_y = np.max([max_x, np.max(x_points)]), np.max([max_y, np.max(y_points)]) if xfactor is not None: delta_x = max_x - min_x min_x, max_x = min_x - xfactor * delta_x, max_x + xfactor * delta_x if yfactor is not None: delta_y = max_y - min_y min_y, max_y = min_y - yfactor * delta_y, max_y + yfactor * delta_y return ([min_x, max_x], [min_y, max_y])