def create_fake_dns_workspace(wsname, angle=-7.53, flipper='ON', dataY=None, loadinstrument=False): """ creates DNS workspace with fake data @param angle Angle of detector bank rotation @param flipper Flipper state (ON/OFF) @param dataY Data array to set as DataY of the created workspace, will be set to np.ones if None @param loadinstrument If True api.LoadInstrument will be executed, needed for DNSMergeRuns """ ndet = 24 dataX = np.zeros(2*ndet) dataX.fill(4.2 + 0.00001) dataX[::2] -= 0.000002 if dataY is None: dataY = np.ones(ndet) dataE = np.sqrt(dataY) # create workspace api.CreateWorkspace(OutputWorkspace=wsname, DataX=dataX, DataY=dataY, DataE=dataE, NSpec=ndet, UnitX="Wavelength") outws = api.mtd[wsname] p_names = 'deterota,wavelength,slit_i_left_blade_position,slit_i_right_blade_position,normalized,\ slit_i_lower_blade_position,slit_i_upper_blade_position,polarisation,polarisation_comment,flipper' p_values = str(angle) + ',4.2,10,10,duration,5,20,x,7a,' + flipper api.AddSampleLogMultiple(Workspace=outws, LogNames=p_names, LogValues=p_values, ParseType=True) # rotate instrument component if loadinstrument: api.LoadInstrument(outws, InstrumentName='DNS', RewriteSpectraMap=True) api.RotateInstrumentComponent(outws, "bank0", X=0, Y=1, Z=0, Angle=angle) return outws
def adjustComponent(self, component, pos, rot): wks_name = self.wks_name if pos: msa.MoveInstrumentComponent(wks_name, component, X=pos[0], Y=pos[1], Z=pos[2], RelativePosition=False) if rot: (rotw, rotx, roty, rotz) = eulerToAngleAxis(rot[0], rot[1], rot[2], self.eulerConvention) msa.RotateInstrumentComponent( wks_name, component, X=rotx, Y=roty, Z=rotz, Angle=rotw, RelativeRotation=False) return
def _minimisation_func(self, x_0, wks_name, component, firstIndex, lastIndex, difc, mask): """ Basic minimization function used. Returns the chisquared difference between the expected difc and the new difc after the component has been moved or rotated. """ xmap = self._mapOptions(x_0) if self._move: api.MoveInstrumentComponent(wks_name, component, X=xmap[0], Y=xmap[1], Z=xmap[2], RelativePosition=False) if self._rotate: (rotw, rotx, roty, rotz) = self._eulerToAngleAxis(xmap[3], xmap[4], xmap[5], self._eulerConvention) # YZX api.RotateInstrumentComponent(wks_name, component, X=rotx, Y=roty, Z=rotz, Angle=rotw, RelativeRotation=False) api.CalculateDIFC(InputWorkspace=wks_name, OutputWorkspace=wks_name) difc_new = api.mtd[wks_name].extractY().flatten( )[firstIndex:lastIndex + 1] if self._masking: difc_new = np.ma.masked_array(difc_new, mask) return chisquare(f_obs=difc, f_exp=difc_new)[0]
def test_DNSTwoTheta(self): outputWorkspaceName = "DNSFlippingRatioCorrTest_Test5" # rotate detector bank to different angles dataws_sf = self.__sf_nicrws - self.__sf_bkgrws dataws_nsf = self.__nsf_nicrws - self.__nsf_bkgrws wslist = [dataws_sf, dataws_nsf, self.__sf_nicrws, self.__nsf_nicrws, self.__sf_bkgrws, self.__nsf_bkgrws] for wks in wslist: api.LoadInstrument(wks, InstrumentName='DNS', RewriteSpectraMap=True) api.RotateInstrumentComponent(dataws_sf, "bank0", X=0, Y=1, Z=0, Angle=-7.53) api.RotateInstrumentComponent(dataws_nsf, "bank0", X=0, Y=1, Z=0, Angle=-7.53) api.RotateInstrumentComponent(self.__sf_nicrws, "bank0", X=0, Y=1, Z=0, Angle=-8.02) api.RotateInstrumentComponent(self.__nsf_nicrws, "bank0", X=0, Y=1, Z=0, Angle=-8.02) api.RotateInstrumentComponent(self.__sf_bkgrws, "bank0", X=0, Y=1, Z=0, Angle=-8.54) api.RotateInstrumentComponent(self.__nsf_bkgrws, "bank0", X=0, Y=1, Z=0, Angle=-8.54) # apply correction alg_test = run_algorithm("DNSFlippingRatioCorr", SFDataWorkspace=dataws_sf, NSFDataWorkspace=dataws_nsf, SFNiCrWorkspace=self.__sf_nicrws.getName(), NSFNiCrWorkspace=self.__nsf_nicrws.getName(), SFBkgrWorkspace=self.__sf_bkgrws.getName(), NSFBkgrWorkspace=self.__nsf_bkgrws.getName(), SFOutputWorkspace=outputWorkspaceName+'SF', NSFOutputWorkspace=outputWorkspaceName+'NSF') self.assertTrue(alg_test.isExecuted()) ws_sf = AnalysisDataService.retrieve(outputWorkspaceName + 'SF') ws_nsf = AnalysisDataService.retrieve(outputWorkspaceName + 'NSF') # dimensions self.assertEqual(24, ws_sf.getNumberHistograms()) self.assertEqual(24, ws_nsf.getNumberHistograms()) self.assertEqual(2, ws_sf.getNumDims()) self.assertEqual(2, ws_nsf.getNumDims()) # 2theta angles must not change after correction has been applied tthetas = np.array([7.53 + i*5 for i in range(24)]) for i in range(24): det = ws_sf.getDetector(i) self.assertAlmostEqual(tthetas[i], np.degrees(ws_sf.detectorSignedTwoTheta(det))) det = ws_nsf.getDetector(i) self.assertAlmostEqual(tthetas[i], np.degrees(ws_nsf.detectorSignedTwoTheta(det))) run_algorithm("DeleteWorkspace", Workspace=outputWorkspaceName + 'SF') run_algorithm("DeleteWorkspace", Workspace=outputWorkspaceName + 'NSF') run_algorithm("DeleteWorkspace", Workspace=dataws_sf) run_algorithm("DeleteWorkspace", Workspace=dataws_nsf) return
def PyExec(self): self._eulerConvention = self.getProperty('EulerConvention').value calWS = self.getProperty('CalibrationTable').value calWS = api.SortTableWorkspace(calWS, Columns='detid') maskWS = self.getProperty("MaskWorkspace").value difc = calWS.column('difc') if maskWS is not None: self._masking = True mask = maskWS.extractY().flatten() difc = np.ma.masked_array(difc, mask) detID = calWS.column('detid') if self.getProperty("Workspace").value is not None: wks_name = self.getProperty("Workspace").value.name() else: wks_name = "alignedWorkspace" api.LoadEmptyInstrument( Filename=self.getProperty("InstrumentFilename").value, OutputWorkspace=wks_name) # Make a dictionary of what options are being refined for sample/source. No rotation. for opt in self._optionsList[:3]: self._optionsDict[opt] = self.getProperty(opt).value for opt in self._optionsList[3:]: self._optionsDict[opt] = False # First fit L1 if selected for Source and/or Sample for component in "Source", "Sample": if self.getProperty("Fit" + component + "Position").value: self._move = True if component == "Sample": comp = api.mtd[wks_name].getInstrument().getSample() else: comp = api.mtd[wks_name].getInstrument().getSource() componentName = comp.getFullName() logger.notice("Working on " + componentName + " Starting position is " + str(comp.getPos())) firstIndex = 0 lastIndex = len(difc) if self._masking: mask_out = mask[firstIndex:lastIndex + 1] else: mask_out = None self._initialPos = [ comp.getPos().getX(), comp.getPos().getY(), comp.getPos().getZ(), 0, 0, 0 ] # Set up x0 and bounds lists x0List = [] boundsList = [] for iopt, opt in enumerate(self._optionsList[:3]): if self._optionsDict[opt]: x0List.append(self._initialPos[iopt]) boundsList.append( (self._initialPos[iopt] + self.getProperty("Min" + opt).value, self._initialPos[iopt] + self.getProperty("Max" + opt).value)) results = minimize(self._minimisation_func, x0=x0List, method='L-BFGS-B', args=(wks_name, componentName, firstIndex, lastIndex, difc[firstIndex:lastIndex + 1], mask_out), bounds=boundsList) # Apply the results to the output workspace xmap = self._mapOptions(results.x) # Need to grab the component again, as things have changed api.MoveInstrumentComponent(wks_name, componentName, X=xmap[0], Y=xmap[1], Z=xmap[2], RelativePosition=False) comp = api.mtd[wks_name].getInstrument().getComponentByName( componentName) logger.notice("Finished " + componentName + " Final position is " + str(comp.getPos())) self._move = False # Now fit all the components if any components = self.getProperty("ComponentList").value # Make a dictionary of what options are being refined. for opt in self._optionsList: self._optionsDict[opt] = self.getProperty(opt).value self._move = (self._optionsDict["Xposition"] or self._optionsDict["Yposition"] or self._optionsDict["Zposition"]) self._rotate = (self._optionsDict["AlphaRotation"] or self._optionsDict["BetaRotation"] or self._optionsDict["GammaRotation"]) prog = Progress(self, start=0, end=1, nreports=len(components)) for component in components: comp = api.mtd[wks_name].getInstrument().getComponentByName( component) firstDetID = self._getFirstDetID(comp) firstIndex = detID.index(firstDetID) lastDetID = self._getLastDetID(comp) lastIndex = detID.index(lastDetID) if lastDetID - firstDetID != lastIndex - firstIndex: raise RuntimeError( "Calibration detid doesn't match instrument") eulerAngles = comp.getRotation().getEulerAngles( self._eulerConvention) logger.notice("Working on " + comp.getFullName() + " Starting position is " + str(comp.getPos()) + " Starting rotation is " + str(eulerAngles)) x0List = [] self._initialPos = [ comp.getPos().getX(), comp.getPos().getY(), comp.getPos().getZ(), eulerAngles[0], eulerAngles[1], eulerAngles[2] ] boundsList = [] if self._masking: mask_out = mask[firstIndex:lastIndex + 1] if mask_out.sum() == mask_out.size: self.log().warning( "All pixels in '%s' are masked. Skipping calibration." % component) continue else: mask_out = None for iopt, opt in enumerate(self._optionsList): if self._optionsDict[opt]: x0List.append(self._initialPos[iopt]) boundsList.append((self._initialPos[iopt] + self.getProperty("Min" + opt).value, self._initialPos[iopt] + self.getProperty("Max" + opt).value)) results = minimize(self._minimisation_func, x0=x0List, method='L-BFGS-B', args=(wks_name, component, firstIndex, lastIndex, difc[firstIndex:lastIndex + 1], mask_out), bounds=boundsList) # Apply the results to the output workspace xmap = self._mapOptions(results.x) if self._move: api.MoveInstrumentComponent(wks_name, component, X=xmap[0], Y=xmap[1], Z=xmap[2], RelativePosition=False) if self._rotate: (rotw, rotx, roty, rotz) = self._eulerToAngleAxis(xmap[3], xmap[4], xmap[5], self._eulerConvention) api.RotateInstrumentComponent(wks_name, component, X=rotx, Y=roty, Z=rotz, Angle=rotw, RelativeRotation=False) # Need to grab the component again, as things have changed comp = api.mtd[wks_name].getInstrument().getComponentByName( component) logger.notice( "Finshed " + comp.getFullName() + " Final position is " + str(comp.getPos()) + " Final rotation is " + str(comp.getRotation().getEulerAngles(self._eulerConvention))) prog.report() logger.notice("Results applied to workspace " + wks_name)
def PyExec(self): # Input filename = self.getPropertyValue("Filename") outws_name = self.getPropertyValue("OutputWorkspace") norm = self.getPropertyValue("Normalization") # load data array from the given file data_array = np.loadtxt(filename) if not data_array.size: message = "File " + filename + " does not contain any data!" self.log().error(message) raise RuntimeError(message) # sample logs logs = {"names": [], "values": [], "units": []} # load run information metadata = DNSdata() try: metadata.read_legacy(filename) except RuntimeError as err: message = "Error of loading of file " + filename + ": " + str(err) self.log().error(message) raise RuntimeError(message) tmp = api.LoadEmptyInstrument(InstrumentName='DNS') self.instrument = tmp.getInstrument() api.DeleteWorkspace(tmp) # load polarisation table and determine polarisation poltable = self.get_polarisation_table() pol = self.get_polarisation(metadata, poltable) if not pol: pol = ['0', 'undefined'] self.log().warning("Failed to determine polarisation for " + filename + ". Values have been set to undefined.") ndet = 24 unitX = "Wavelength" if metadata.tof_channel_number < 2: dataX = np.zeros(2 * ndet) dataX.fill(metadata.wavelength + 0.00001) dataX[::2] -= 0.000002 else: unitX = "TOF" # get instrument parameters l1 = np.linalg.norm(self.instrument.getSample().getPos() - self.instrument.getSource().getPos()) self.log().notice("L1 = {} m".format(l1)) dt_factor = float( self.instrument.getStringParameter("channel_width_factor")[0]) # channel width dt = metadata.tof_channel_width * dt_factor # calculate tof1 velocity = h / (m_n * metadata.wavelength * 1e-10) # m/s tof1 = 1e+06 * l1 / velocity # microseconds self.log().debug("TOF1 = {} microseconds".format(tof1)) self.log().debug("Delay time = {} microsecond".format( metadata.tof_delay_time)) # create dataX array x0 = tof1 + metadata.tof_delay_time self.log().debug("TOF1 = {} microseconds".format(tof1)) dataX = np.linspace(x0, x0 + metadata.tof_channel_number * dt, metadata.tof_channel_number + 1) # sample logs logs["names"].extend( ["channel_width", "TOF1", "delay_time", "tof_channels"]) logs["values"].extend([ dt, tof1, metadata.tof_delay_time, metadata.tof_channel_number ]) logs["units"].extend( ["microseconds", "microseconds", "microseconds", ""]) if metadata.tof_elastic_channel: logs["names"].append("EPP") logs["values"].append(metadata.tof_elastic_channel) logs["units"].append("") if metadata.chopper_rotation_speed: logs["names"].append("chopper_speed") logs["values"].append(metadata.chopper_rotation_speed) logs["units"].append("Hz") if metadata.chopper_slits: logs["names"].append("chopper_slits") logs["values"].append(metadata.chopper_slits) logs["units"].append("") # data normalization factor = 1.0 yunit = "Counts" ylabel = "Intensity" if norm == 'duration': factor = metadata.duration yunit = "Counts/s" ylabel = "Intensity normalized to duration" if factor <= 0: raise RuntimeError("Duration is invalid for file " + filename + ". Cannot normalize.") if norm == 'monitor': factor = metadata.monitor_counts yunit = "Counts/monitor" ylabel = "Intensity normalized to monitor" if factor <= 0: raise RuntimeError("Monitor counts are invalid for file " + filename + ". Cannot normalize.") # set values for dataY and dataE dataY = data_array[0:ndet, 1:] / factor dataE = np.sqrt(data_array[0:ndet, 1:]) / factor # create workspace api.CreateWorkspace(OutputWorkspace=outws_name, DataX=dataX, DataY=dataY, DataE=dataE, NSpec=ndet, UnitX=unitX) outws = api.AnalysisDataService.retrieve(outws_name) api.LoadInstrument(outws, InstrumentName='DNS', RewriteSpectraMap=True) run = outws.mutableRun() if metadata.start_time and metadata.end_time: run.setStartAndEndTime(DateAndTime(metadata.start_time), DateAndTime(metadata.end_time)) # add name of file as a run title fname = os.path.splitext(os.path.split(filename)[1])[0] run.addProperty('run_title', fname, True) # rotate the detector bank to the proper position api.RotateInstrumentComponent(outws, "bank0", X=0, Y=1, Z=0, Angle=metadata.deterota) # add sample log Ei and wavelength logs["names"].extend(["Ei", "wavelength"]) logs["values"].extend([metadata.incident_energy, metadata.wavelength]) logs["units"].extend(["meV", "Angstrom"]) # add other sample logs logs["names"].extend([ "deterota", "mon_sum", "duration", "huber", "omega", "T1", "T2", "Tsp" ]) logs["values"].extend([ metadata.deterota, metadata.monitor_counts, metadata.duration, metadata.huber, metadata.huber - metadata.deterota, metadata.temp1, metadata.temp2, metadata.tsp ]) logs["units"].extend([ "Degrees", "Counts", "Seconds", "Degrees", "Degrees", "K", "K", "K" ]) # flipper, coil currents and polarisation flipper_status = 'OFF' # flipper OFF if abs(metadata.flipper_precession_current) > sys.float_info.epsilon: flipper_status = 'ON' # flipper ON logs["names"].extend([ "flipper_precession", "flipper_z_compensation", "flipper", "C_a", "C_b", "C_c", "C_z", "polarisation", "polarisation_comment" ]) logs["values"].extend([ metadata.flipper_precession_current, metadata.flipper_z_compensation_current, flipper_status, metadata.a_coil_current, metadata.b_coil_current, metadata.c_coil_current, metadata.z_coil_current, str(pol[0]), str(pol[1]) ]) logs["units"].extend(["A", "A", "", "A", "A", "A", "A", "", ""]) # slits logs["names"].extend([ "slit_i_upper_blade_position", "slit_i_lower_blade_position", "slit_i_left_blade_position", "slit_i_right_blade_position" ]) logs["values"].extend([ metadata.slit_i_upper_blade_position, metadata.slit_i_lower_blade_position, metadata.slit_i_left_blade_position, metadata.slit_i_right_blade_position ]) logs["units"].extend(["mm", "mm", "mm", "mm"]) # add information whether the data are normalized (duration/monitor/no): api.AddSampleLog(outws, LogName='normalized', LogText=norm, LogType='String') api.AddSampleLogMultiple(outws, LogNames=logs["names"], LogValues=logs["values"], LogUnits=logs["units"]) outws.setYUnit(yunit) outws.setYUnitLabel(ylabel) self.setProperty("OutputWorkspace", outws) self.log().debug('LoadDNSLegacy: data are loaded to the workspace ' + outws_name) return
def PyExec(self): # Input filename = self.getPropertyValue("Filename") outws_name = self.getPropertyValue("OutputWorkspace") norm = self.getPropertyValue("Normalization") # load data array from the given file data_array = np.loadtxt(filename) if not data_array.size: message = "File " + filename + " does not contain any data!" self.log().error(message) raise RuntimeError(message) # load run information metadata = DNSdata() try: metadata.read_legacy(filename) except RuntimeError as err: message = "Error of loading of file " + filename + ": " + str(err) self.log().error(message) raise RuntimeError(message) # load polarisation table and determine polarisation poltable = self.get_polarisation_table() pol = self.get_polarisation(metadata, poltable) if not pol: pol = ['0', 'undefined'] self.log().warning("Failed to determine polarisation for " + filename + ". Values have been set to undefined.") ndet = 24 # this needed to be able to use ConvertToMD dataX = np.zeros(2*ndet) dataX.fill(metadata.wavelength + 0.00001) dataX[::2] -= 0.000002 # data normalization factor = 1.0 yunit = "Counts" ylabel = "Intensity" if norm == 'duration': factor = metadata.duration yunit = "Counts/s" ylabel = "Intensity normalized to duration" if factor <= 0: raise RuntimeError("Duration is invalid for file " + filename + ". Cannot normalize.") if norm == 'monitor': factor = metadata.monitor_counts yunit = "Counts/monitor" ylabel = "Intensity normalized to monitor" if factor <= 0: raise RuntimeError("Monitor counts are invalid for file " + filename + ". Cannot normalize.") # set values for dataY and dataE dataY = data_array[0:ndet, 1:]/factor dataE = np.sqrt(data_array[0:ndet, 1:])/factor # create workspace api.CreateWorkspace(OutputWorkspace=outws_name, DataX=dataX, DataY=dataY, DataE=dataE, NSpec=ndet, UnitX="Wavelength") outws = api.AnalysisDataService.retrieve(outws_name) api.LoadInstrument(outws, InstrumentName='DNS', RewriteSpectraMap=True) run = outws.mutableRun() if metadata.start_time and metadata.end_time: run.setStartAndEndTime(DateAndTime(metadata.start_time), DateAndTime(metadata.end_time)) # add name of file as a run title fname = os.path.splitext(os.path.split(filename)[1])[0] run.addProperty('run_title', fname, True) # rotate the detector bank to the proper position api.RotateInstrumentComponent(outws, "bank0", X=0, Y=1, Z=0, Angle=metadata.deterota) # add sample log Ei and wavelength api.AddSampleLog(outws, LogName='Ei', LogText=str(metadata.incident_energy), LogType='Number', LogUnit='meV') api.AddSampleLog(outws, LogName='wavelength', LogText=str(metadata.wavelength), LogType='Number', LogUnit='Angstrom') # add other sample logs api.AddSampleLog(outws, LogName='deterota', LogText=str(metadata.deterota), LogType='Number', LogUnit='Degrees') api.AddSampleLog(outws, 'mon_sum', LogText=str(float(metadata.monitor_counts)), LogType='Number') api.AddSampleLog(outws, LogName='duration', LogText=str(metadata.duration), LogType='Number', LogUnit='Seconds') api.AddSampleLog(outws, LogName='huber', LogText=str(metadata.huber), LogType='Number', LogUnit='Degrees') api.AddSampleLog(outws, LogName='omega', LogText=str(metadata.huber - metadata.deterota), LogType='Number', LogUnit='Degrees') api.AddSampleLog(outws, LogName='T1', LogText=str(metadata.temp1), LogType='Number', LogUnit='K') api.AddSampleLog(outws, LogName='T2', LogText=str(metadata.temp2), LogType='Number', LogUnit='K') api.AddSampleLog(outws, LogName='Tsp', LogText=str(metadata.tsp), LogType='Number', LogUnit='K') # flipper api.AddSampleLog(outws, LogName='flipper_precession', LogText=str(metadata.flipper_precession_current), LogType='Number', LogUnit='A') api.AddSampleLog(outws, LogName='flipper_z_compensation', LogText=str(metadata.flipper_z_compensation_current), LogType='Number', LogUnit='A') flipper_status = 'OFF' # flipper OFF if abs(metadata.flipper_precession_current) > sys.float_info.epsilon: flipper_status = 'ON' # flipper ON api.AddSampleLog(outws, LogName='flipper', LogText=flipper_status, LogType='String') # coil currents api.AddSampleLog(outws, LogName='C_a', LogText=str(metadata.a_coil_current), LogType='Number', LogUnit='A') api.AddSampleLog(outws, LogName='C_b', LogText=str(metadata.b_coil_current), LogType='Number', LogUnit='A') api.AddSampleLog(outws, LogName='C_c', LogText=str(metadata.c_coil_current), LogType='Number', LogUnit='A') api.AddSampleLog(outws, LogName='C_z', LogText=str(metadata.z_coil_current), LogType='Number', LogUnit='A') # type of polarisation api.AddSampleLog(outws, 'polarisation', LogText=pol[0], LogType='String') api.AddSampleLog(outws, 'polarisation_comment', LogText=str(pol[1]), LogType='String') # slits api.AddSampleLog(outws, LogName='slit_i_upper_blade_position', LogText=str(metadata.slit_i_upper_blade_position), LogType='Number', LogUnit='mm') api.AddSampleLog(outws, LogName='slit_i_lower_blade_position', LogText=str(metadata.slit_i_lower_blade_position), LogType='Number', LogUnit='mm') api.AddSampleLog(outws, LogName='slit_i_left_blade_position', LogText=str(metadata.slit_i_left_blade_position), LogType='Number', LogUnit='mm') api.AddSampleLog(outws, 'slit_i_right_blade_position', LogText=str(metadata.slit_i_right_blade_position), LogType='Number', LogUnit='mm') # data normalization # add information whether the data are normalized (duration/monitor/no): api.AddSampleLog(outws, LogName='normalized', LogText=norm, LogType='String') outws.setYUnit(yunit) outws.setYUnitLabel(ylabel) self.setProperty("OutputWorkspace", outws) self.log().debug('LoadDNSLegacy: data are loaded to the workspace ' + outws_name) return
print("=========================================") # Create workspace for current frame ws_for_this_frame = createWorkspace(data_x=[f.wavelength], data_y=f.IntensityUp, data_e=np.sqrt(f.IntensityUp), n_spec=f.x*f.y, unit="Wavelength") # Register the workspace in the mantid ADS mantid.mtd.addOrReplace("ws_for_this_frame", ws_for_this_frame) # Load the instrument from the definition file mantid.LoadInstrument(ws_for_this_frame, FileName="5C1_Definition.xml", RewriteSpectraMap=True) # Rotate the instrument to the current gamma angle (in degrees) mantid.RotateInstrumentComponent(ws_for_this_frame, "detector_panel", X=0, Y=1, Z=0, Angle=f.Gamma, RelativeRotation=False) # Normalise by monitor counts normalised = ws_for_this_frame / f.totalmonitorcount # Convert to d-spacing dspacing = mantid.ConvertUnits(InputWorkspace=normalised, Target="dSpacing", EMode="Elastic") # 1. Naive accumulation of workspace data: this does not seem to work rebinned = mantid.Rebin(dspacing, "0.5,0.01,5.0") if final is None: final = mantid.CloneWorkspace(rebinned) else: final += rebinned # 2. Try to extract 1D profile from equatorial plane
def _minimisation_func(self, x_0, wks_name, component, firstIndex, lastIndex): """ Basic minimization function used. Returns the sum of the absolute values for the fractional peak deviations: .. math:: \\sum_i^{N_d}\\sum_j^{N_p} (1 - m_{i,j}) \\frac{|d_{i,j} - d_j^*|}{d_j^*} where :math:`N_d` is the number of detectors in the bank, :math:`N_p` is the number of reference peaks, and :math:`m_{i,j}` is the mask for peak :math:`j` and detector :math:`i`. The mask evaluates to 1 if the detector is defective or the peak is missing in the detector, otherwise the mask evaluates to zero. There's an implicit one-to-correspondence between array index of ``difc`` and workspace index of ``wks_name``, that is, between row index of the input TOFS table and workspace index of ``wks_name``. @param x_0 :: list of length 3 (new XYZ coordinates of the component) or length 6 (XYZ and rotation coords) @param wks_name :: name of a workspace with an embedded instrument. The instrument will be adjusted according to the new coordinates ``x_0`` for instrument component ``component``. It's pixel spectra will contain the new DIFC @param component :: name of the instrument component to be optimized @param firstIndex :: workspace index of first index of ``difc`` array to be considered when comparing old and new DIFC values. When fitting the source or sample, this is the first spectrum index. @param lastIndex :: workspace index of last index of ``difc`` array to be considered when comparing old and new DIFC values. When fitting the source or sample, this is the last row number of the input TOFS table. @return Chi-square value between old and new DIFC values for the unmasked spectra """ xmap = self._mapOptions( x_0) # pad null rotations when x_0 contains only translations if self._move: api.MoveInstrumentComponent(wks_name, component, X=xmap[0], Y=xmap[1], Z=xmap[2], RelativePosition=False, EnableLogging=False) if self._rotate: (rotw, rotx, roty, rotz) = self._eulerToAngleAxis(xmap[3], xmap[4], xmap[5], self._eulerConvention) # YZX api.RotateInstrumentComponent(wks_name, component, X=rotx, Y=roty, Z=rotz, Angle=rotw, RelativeRotation=False, EnableLogging=False) api.CalculateDIFC(InputWorkspace=wks_name, OutputWorkspace=wks_name, EnableLogging=False) difc = api.mtd[wks_name].extractY().flatten()[firstIndex:lastIndex + 1] peaks_d = self.peaks_tof[ firstIndex:lastIndex + 1] / difc[:, np.newaxis] # peak centers in d-spacing units # calculate the fractional peak center deviations, then sum their absolute values return np.sum(np.abs((peaks_d - self.peaks_ref) / self.peaks_ref))
def PyExec(self): table_tof = self.getProperty('PeakCentersTofTable').value self.peaks_tof = self._extract_tofs(table_tof) detector_count, peak_count = self.peaks_tof.shape table_tof = api.SortTableWorkspace(table_tof, Columns='detid') detID = table_tof.column('detid') peaks_ref = np.sort(self.getProperty( 'PeakPositions').value) # sort by increasing value self.peaks_ref = peaks_ref[np.newaxis, :] # shape = (1, peak_count) # Process input mask maskWS = self.getProperty("MaskWorkspace").value if maskWS is not None: mask = maskWS.extractY().flatten() # shape=(detector_count,) peaks_mask = np.tile( mask[:, np.newaxis], peak_count) # shape=(detector_count, peak_count) else: peaks_mask = np.zeros( (detector_count, peak_count)) # no detectors are masked peaks_mask[np.isnan(self.peaks_tof)] = True # mask the defective detectors and missing peaks self.peaks_tof = np.ma.masked_array(self.peaks_tof, peaks_mask) input_workspace = self.getProperty('InputWorkspace').value # Table containing the optimized absolute locations and orientations for each component adjustments_table_name = self.getProperty('AdjustmentsTable').value if len(adjustments_table_name) > 0: adjustments_table = self._initialize_adjustments_table( adjustments_table_name) saving_adjustments = True else: saving_adjustments = False # Table containing the relative changes in position and euler angles for each bank component displacements_table_name = self.getProperty('DisplacementsTable').value if len(displacements_table_name) > 0: displacements_table = self._initialize_displacements_table( displacements_table_name) saving_displacements = True else: saving_displacements = False self._eulerConvention = self.getProperty('EulerConvention').value output_workspace = self.getPropertyValue("OutputWorkspace") wks_name = '__alignedworkspace' # workspace whose counts will be DIFC values if bool(input_workspace) is True: api.CloneWorkspace(InputWorkspace=input_workspace, OutputWorkspace=wks_name) if output_workspace != str(input_workspace): api.CloneWorkspace(InputWorkspace=input_workspace, OutputWorkspace=output_workspace) else: api.LoadEmptyInstrument( Filename=self.getProperty("InstrumentFilename").value, OutputWorkspace=wks_name) # Make a dictionary of what options are being refined for sample/source. No rotation. for translation_option in self._optionsList[:3]: self._optionsDict[translation_option] = self.getProperty( translation_option).value for rotation_option in self._optionsList[3:]: self._optionsDict[rotation_option] = False # First fit L1 if selected for Source and/or Sample sample_position_begin = api.mtd[wks_name].getInstrument().getSample( ).getPos() for component in "Source", "Sample": # fit first the source position, then the sample position if self.getProperty("Fit" + component + "Position").value: self._move = True if component == "Sample": comp = api.mtd[wks_name].getInstrument().getSample() else: comp = api.mtd[wks_name].getInstrument().getSource() componentName = comp.getFullName() logger.notice("Working on " + componentName + " Starting position is " + str(comp.getPos())) firstIndex = 0 lastIndex = detector_count - 1 self._initialPos = [ comp.getPos().getX(), comp.getPos().getY(), comp.getPos().getZ(), 0, 0, 0 ] # no rotation # Set up x0 and bounds lists x0List = [] # initial X, Y, Z coordinates boundsList = [] # [(minX, maxX), (minZ, maxZ), (minZ, maxZ)] for iopt, translation_option in enumerate( self._optionsList[:3]): # iterate over X, Y, and Z if self._optionsDict[translation_option]: x0List.append(self._initialPos[iopt]) # default range for X is (x0 - 0.1m, x0 + 0.1m), same for Y and Z boundsList.append(( self._initialPos[iopt] + self.getProperty("Min" + translation_option).value, self._initialPos[iopt] + self.getProperty("Max" + translation_option).value)) # scipy.opimize.minimize with the L-BFGS-B algorithm results: OptimizeResult = minimize( self._minimisation_func, x0=x0List, method='L-BFGS-B', args=(wks_name, componentName, firstIndex, lastIndex), bounds=boundsList) # Apply the results to the output workspace xmap = self._mapOptions(results.x) # Save translation and rotations, if requested if saving_adjustments: instrument = api.mtd[wks_name].getInstrument() name_finder = { 'Source': instrument.getSource().getName(), 'Sample': instrument.getSample().getName() } component_adjustments = [ name_finder[component] ] + xmap[:3] + [0.0] * 4 # no rotations adjustments_table.addRow(component_adjustments) # Need to grab the component again, as things have changed kwargs = dict(X=xmap[0], Y=xmap[1], Z=xmap[2], RelativePosition=False, EnableLogging=False) api.MoveInstrumentComponent(wks_name, componentName, **kwargs) # adjust workspace api.MoveInstrumentComponent(output_workspace, componentName, **kwargs) # adjust workspace comp = api.mtd[wks_name].getInstrument().getComponentByName( componentName) logger.notice("Finished " + componentName + " Final position is " + str(comp.getPos())) self._move = False sample_position_end = api.mtd[wks_name].getInstrument().getSample( ).getPos() # Now fit all the remaining components, if any components = self.getProperty("ComponentList").value # Make a dictionary of what translational and rotational options are being refined. for opt in self._optionsList: self._optionsDict[opt] = self.getProperty(opt).value self._move = any([ self._optionsDict[t] for t in ('Xposition', 'Yposition', 'Zposition') ]) self._rotate = any([ self._optionsDict[r] for r in ('AlphaRotation', 'BetaRotation', 'GammaRotation') ]) prog = Progress(self, start=0, end=1, nreports=len(components)) for component in components: comp = api.mtd[wks_name].getInstrument().getComponentByName( component) firstDetID = self._getFirstDetID(comp) firstIndex = detID.index( firstDetID) # a row index in the input TOFS table lastDetID = self._getLastDetID(comp) lastIndex = detID.index( lastDetID) # a row index in the input TOFS table if lastDetID - firstDetID != lastIndex - firstIndex: raise RuntimeError("TOFS detid doesn't match instrument") eulerAngles: List[float] = comp.getRotation().getEulerAngles( self._eulerConvention) logger.notice("Working on " + comp.getFullName() + " Starting position is " + str(comp.getPos()) + " Starting rotation is " + str(eulerAngles)) x0List = [] self._initialPos = [ comp.getPos().getX(), comp.getPos().getY(), comp.getPos().getZ(), eulerAngles[0], eulerAngles[1], eulerAngles[2] ] # Distance between the original position of the sample and the original position of the component comp_sample_distance_begin = (comp.getPos() - sample_position_begin).norm() boundsList = [] if np.all(peaks_mask[firstIndex:lastIndex + 1].astype(bool)): self.log().warning( "All pixels in '%s' are masked. Skipping calibration." % component) continue for iopt, opt in enumerate(self._optionsList): if self._optionsDict[opt]: x0List.append(self._initialPos[iopt]) boundsList.append((self._initialPos[iopt] + self.getProperty("Min" + opt).value, self._initialPos[iopt] + self.getProperty("Max" + opt).value)) minimizer_selection = self.getProperty('Minimizer').value if minimizer_selection == 'L-BFGS-B': # scipy.opimize.minimize with the L-BFGS-B algorithm results: OptimizeResult = minimize(self._minimisation_func, x0=x0List, method='L-BFGS-B', args=(wks_name, component, firstIndex, lastIndex), bounds=boundsList) elif minimizer_selection == 'differential_evolution': results: OptimizeResult = differential_evolution( self._minimisation_func, bounds=boundsList, args=(wks_name, component, firstIndex, lastIndex), maxiter=self.getProperty('MaxIterations').value) # Apply the results to the output workspace xmap = self._mapOptions(results.x) comp = api.mtd[wks_name].getInstrument().getComponentByName( component) # adjusted component # Distance between the adjusted position of the sample and the adjusted position of the component comp_sample_distance_end = (comp.getPos() - sample_position_end).norm() component_adjustments = [ 0. ] * 7 # 3 for translation, 3 for rotation axis, 1 for rotation angle component_displacements = [ 0. ] * 7 # 1 for distnace, 3 for translation, 3 for Euler angles component_displacements[0] = 1000 * ( comp_sample_distance_end - comp_sample_distance_begin ) # in mili-meters if self._move: kwargs = dict(X=xmap[0], Y=xmap[1], Z=xmap[2], RelativePosition=False, EnableLogging=False) api.MoveInstrumentComponent(wks_name, component, **kwargs) # adjust workspace api.MoveInstrumentComponent(output_workspace, component, **kwargs) # adjust workspace component_adjustments[:3] = xmap[:3] for i in range(3): component_displacements[i + 1] = 1000 * ( xmap[i] - self._initialPos[i]) # in mili-meters if self._rotate: (rotw, rotx, roty, rotz) = self._eulerToAngleAxis(xmap[3], xmap[4], xmap[5], self._eulerConvention) kwargs = dict(X=rotx, Y=roty, Z=rotz, Angle=rotw, RelativeRotation=False, EnableLogging=False) api.RotateInstrumentComponent(wks_name, component, **kwargs) # adjust workspace api.RotateInstrumentComponent(output_workspace, component, **kwargs) # adjust workspace component_adjustments[3:] = [rotx, roty, rotz, rotw] for i in range(3, 6): component_displacements[ i + 1] = xmap[i] - self._initialPos[i] # in degrees if saving_adjustments and (self._move or self._rotate): adjustments_table.addRow([component] + component_adjustments) if saving_displacements and (self._move or self._rotate): displacements_table.addRow([component] + component_displacements) # Need to grab the component object again, as things have changed logger.notice( "Finished " + comp.getFullName() + " Final position is " + str(comp.getPos()) + " Final rotation is " + str(comp.getRotation().getEulerAngles(self._eulerConvention))) prog.report() api.DeleteWorkspace(wks_name) self.setProperty("OutputWorkspace", output_workspace) logger.notice("Results applied to workspace " + wks_name)