def load_empty(self, workspace_name = None): """ Loads the instrument definition file into a workspace with the given name. If no name is given a hidden workspace is used @param workspace_name: the name of the workspace to create and/or display @return the name of the workspace that was created """ if workspace_name is None: workspace_name = '__'+self._NAME+'_empty' api.LoadEmptyInstrument(Filename=self._definition_file, OutputWorkspace=workspace_name) return workspace_name
def test_advanced_geometry_with_absent_shape(self): import mantid.simpleapi as mantid # single bank 3 by 3 ws = mantid.CreateSampleWorkspace(NumBanks=1, BankPixelWidth=3, StoreInADS=False) # Save and reload trick to purge sample shape info file_name = "example_geometry.nxs" geom_path = os.path.join(tempfile.gettempdir(), file_name) mantid.SaveNexusGeometry(ws, geom_path) # Does not save shape info assert os.path.isfile(geom_path) # sanity check out = mantid.LoadEmptyInstrument( Filename=geom_path, StoreInADS=False) # reload without sample info os.remove(geom_path) assert not out.componentInfo().hasValidShape(0) # sanity check da = scn.mantid.from_mantid(out, advanced_geometry=True) # Shapes have zero size assert sc.identical(sc.sum(da.meta['shape']), sc.vector(value=[0, 0, 0], unit=sc.units.m))
def _load_idf(self, new_idf): # The widget self-destructs when you delete the workspace below, for # some reason, so let's remove it cleanly first. if self._widget is not None: self.layout().removeWidget(self._widget) self._widget = None # If you don't do this there is an Unhandled Exception, which crashes # NICOS completely, when you try to switch setups, with a message of: # > Instrument view: workspace doesn't exist if self._workspace is not None: simpleapi.DeleteWorkspace("ws") self._workspace = None if new_idf is not None: self._workspace = simpleapi.LoadEmptyInstrument( new_idf, OutputWorkspace="ws") self._widget = mpy.MantidQt.MantidWidgets.InstrumentWidget( "ws", self) self.layout().addWidget(self._widget) self._watcher.setClient(self.client) self._current_idf = new_idf
# eulerConvention = 'YZX' wks_name = "alignedWorkspace" # use Si data # idf_orig = os.path.join(msa.ConfigService.getInstrumentDirectory(), 'SEQUOIA_Definition.xml') # special: use C60 data for short packs around forward beam idf_orig = os.path.join('SEQUOIA_Definition_guessshortpacks.xml') # ## Fit twotheta and L2 # $ DIFC = (L1+L2)/(\pi) \; sin(\theta) \times 0.0015882549421289758 \times 10^6 $ #L1 = 20.0965 L1 = 20.0114 sin_theta = difc / (0.0015882549421289758 * 1e6) * np.pi / (L1 + L2) msa.LoadEmptyInstrument(idf_orig, OutputWorkspace=wks_name) instrument_model = align.InstrumentModel(wks_name, detID, mask, eulerConvention="YZX") options = collections.OrderedDict() options['Xposition'] = (-.3, .3) options['Yposition'] = False options['Zposition'] = (-.3, .3) options['AlphaRotation'] = (-2., 2.) options['BetaRotation'] = False options['GammaRotation'] = False brow = ['B%s' % i for i in range(1, 38)] crow = ['C%s' % i for i in range(1, 38)] del crow[24] # no C25
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 validateInputs(self): """ Does basic validation for inputs """ issues = dict() calWS = self.getProperty('CalibrationTable').value if 'difc' not in calWS.getColumnNames( ) or 'detid' not in calWS.getColumnNames(): issues[ 'CalibrationTable'] = "Calibration table requires detid and difc" maskWS = self.getProperty("MaskWorkspace").value if maskWS is not None and maskWS.id() != 'MaskWorkspace': issues[ 'MaskWorkspace'] = "MaskWorkspace must be empty or of type \"MaskWorkspace\"" # Need to get instrument in order to check components are valid if self.getProperty("Workspace").value is not None: wks_name = self.getProperty("Workspace").value.name() else: inputFilename = self.getProperty("InstrumentFilename").value if inputFilename == "": issues[ "Workspace"] = "A Workspace or InstrumentFilename must be defined" return issues else: api.LoadEmptyInstrument(Filename=inputFilename, OutputWorkspace="alignedWorkspace") wks_name = "alignedWorkspace" # Check if each component listed is defined in the instrument components = self.getProperty("ComponentList").value if len(components) <= 0 and not self.getProperty( "FitSourcePosition").value and not self.getProperty( "FitSamplePosition").value: issues['ComponentList'] = "Must supply components" else: components = [ component for component in components if api.mtd[wks_name].getInstrument().getComponentByName( component) is None ] if len(components) > 0: issues['ComponentList'] = "Instrument has no component \"" \ + ','.join(components) + "\"" # This checks that something will actually be refined, if not (self.getProperty("Xposition").value or self.getProperty("Yposition").value or self.getProperty("Zposition").value or self.getProperty("AlphaRotation").value or self.getProperty("BetaRotation").value or self.getProperty("GammaRotation").value): issues["Xposition"] = "You must calibrate at least one parameter." # Check that a position refinement is selected for sample/source if ((self.getProperty("FitSourcePosition").value or self.getProperty("FitSamplePosition").value) and not (self.getProperty("Xposition").value or self.getProperty("Yposition").value or self.getProperty("Zposition").value)): issues[ "Xposition"] = "If fitting source or sample, you must calibrate at least one position parameter." return issues
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 test_from_mantid_LoadEmptyInstrument(self): import mantid.simpleapi as mantid ws = mantid.LoadEmptyInstrument(InstrumentName='PG3') scn.from_mantid(ws)
def align( self, difc, mask, pack='C25T', ofile=open('new.xml', 'wt'), logger=None, params0=None, ): """ difc, mask: for a pack pack: pack name """ logger = logger or self.logger # ## Fit twotheta and L2 # $ DIFC = (L1+L2)/(\pi) \; sin(\theta) \times 0.0015882549421289758 \times 10^6 $ difc, mask = self._prepare_difc_and_mask_from_pack_difc( difc, mask, pack) # combine with L2 mask mask = np.logical_or(mask, self.L2_mask) # L1 = self.L1 L2 = self.L2 # import pdb; pdb.set_trace() # Apply mask difc = np.ma.masked_array(difc, mask) L2 = np.ma.masked_array(L2, mask) # calculate sin(theta) self.sin_theta = sin_theta = difc / (0.0015882549421289758 * 1e6) * np.pi / (L1 + L2) # wks_name = "alignedWorkspace" msa.LoadEmptyInstrument(self.init_IDF, OutputWorkspace=wks_name) instrument_model = align_utils.InstrumentModel( wks_name, self.detIDs, mask, eulerConvention=self.eulerConvention) # options = self.options # print "- Working on %s" % (pack, ) pack_type = pack_types[pack] self.pack_model = pack_model = instrument_model.component( '%s/%s' % (pack, pack_type), type='detpack') print "- pack params:", pack_model.getParams() init_center = pack_model.position() estimate = align_utils.estimate_pack_center_position( sin_theta, L2, pack_model, init_center) # fit = align_utils.FitPackTwothetaAndL2(pack_model, options, sin_theta, L2, logger) fit = align_utils.FitPack_DifcL2(pack_model, options, difc, L2, logger=logger, params0=params0) # fit = align_utils.FitPackDifc(pack_model, options, difc, logger=logger) fit.fit() print "- Estimate:", estimate x, y, z, ry, rz, rx = new_params = pack_model.getParams() print "- New:", new_params s = template.format(pack, pack_type, x, y, z, ry) print s ofile.write(s) return new_params, fit
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)
def validateInputs(self): """ Does basic validation for inputs """ issues = dict() peak_positions = self.getProperty('PeakPositions').value table_tof: TableWorkspace = self.getProperty( 'PeakCentersTofTable').value if 'detid' not in table_tof.getColumnNames(): issues[ 'PeakCentersTofTable'] = 'PeakCentersTofTable is missing column "detid"' # The titles for table columns storing the TOF peak-center positions start with '@' column_names = [ name for name in table_tof.getColumnNames() if name[0] == '@' ] peak_count = len( peak_positions) # number of reference peak-center values if len(column_names) != peak_count: error_message = f'The number of table columns containing the peak center positions' \ f' {len(column_names)} is different than the number of peak positions {peak_count}' issues['PeakCentersTofTable'] = error_message # The titles for table columns storing the TOF peak-center positions do contain the values # of the reference peak-center positions in d-spacing units, up to a precision of 5 def with_precision(the_number, precision): r"""Analog of C++'s std::setprecision""" return round(the_number, precision - len(str(int(the_number)))) for column_name, peak_position in zip(column_names, sorted(peak_positions)): if (float(column_name[1:]) - with_precision(peak_position, 5)) > 1.e-5: issues[ 'PeakCentersTofTable'] = f'{column_name} and {peak_position} differ up to precision 5' maskWS: MaskWorkspace = self.getProperty("MaskWorkspace").value if maskWS is not None: if maskWS.id() != 'MaskWorkspace': issues[ 'MaskWorkspace'] = "MaskWorkspace must be empty or of type \"MaskWorkspace\"" # The mask workspace should contain as many spectra as rows in the TOFS table if maskWS.getNumberHistograms() != table_tof.rowCount(): error_message = 'The mask workspace must contain as many spectra as rows in the TOFS table' issues['MaskWorkspace'] = error_message # Need to get instrument in order to check if components are valid input_workspace = self.getProperty("InputWorkspace").value if bool(input_workspace) is True: wks_name = input_workspace.name() else: inputFilename = self.getProperty("InstrumentFilename").value if inputFilename == "": issues[ "InputWorkspace"] = "A Workspace or InstrumentFilename must be defined" return issues else: wks_name = "__alignedWorkspace" # a temporary workspace api.LoadEmptyInstrument(Filename=inputFilename, OutputWorkspace=wks_name) # Check if each component listed is defined in the instrument components = self.getProperty("ComponentList").value source_or_sample = self.getProperty( "FitSourcePosition").value or self.getProperty( "FitSamplePosition").value if len(components) <= 0 and not source_or_sample: issues['ComponentList'] = "Must supply components" else: get_component = api.mtd[wks_name].getInstrument( ).getComponentByName components = [ component for component in components if get_component(component) is None ] if len(components) > 0: issues['ComponentList'] = "Instrument has no component \"" \ + ','.join(components) + "\"" if wks_name == '__alignedWorkspace': api.DeleteWorkspace( '__alignedWorkspace') # delete temporary workspace # This checks that something will actually be refined, if not (self.getProperty("Xposition").value or self.getProperty("Yposition").value or self.getProperty("Zposition").value or self.getProperty("AlphaRotation").value or self.getProperty("BetaRotation").value or self.getProperty("GammaRotation").value): issues[ "Xposition"] = "You must calibrate at least one position or rotation parameter." # Check that a position refinement is selected for sample/source if ((self.getProperty("FitSourcePosition").value or self.getProperty("FitSamplePosition").value) and not (self.getProperty("Xposition").value or self.getProperty("Yposition").value or self.getProperty("Zposition").value)): issues[ "Xposition"] = "If fitting source or sample, you must calibrate at least one position parameter." return issues