def _report_progress(msg: str, reporter: Union[None, Progress] = None, notice: bool = False) -> None: """ :param msg: message to print out :param reporter: Progress object for visual feedback in Workbench :param notice: Log at "notice" level (i.e. visible by default) """ # In order to avoid # # RuntimeError: Pickling of "mantid.kernel._kernel.Logger" # instances is not enabled (http://www.boost.org/libs/python/doc/v2/pickle.html) # # logger has to be imported locally from mantid.kernel import logger if reporter: reporter.report(msg) if notice: logger.notice(msg) else: logger.information(msg)
def PyExec(self): self._setup() self._calculate_parameters() if not self._dry_run: self._fury() self._add_logs() if self._save: workdir = config['defaultsave.directory'] opath = os.path.join(workdir, self._output_workspace + '.nxs') SaveNexusProcessed(InputWorkspace=self._output_workspace, Filename=opath) if self._verbose: logger.notice('Output file : ' + opath) if self._plot: self._plot_output() else: if self._verbose: logger.notice('Dry run, will not run Fury') self.setProperty('ParameterWorkspace', self._parameter_table) self.setProperty('OutputWorkspace', self._output_workspace)
def run_algorithm(self, name: str, start_progress: float, end_progress: float, soft_crash=True, **kwargs): r""" @param soft_crash : log an error but do not raise an exception """ algorithm_align = self.createChildAlgorithm( name=name, startProgress=start_progress, endProgress=end_progress, enableLogging=True) [ algorithm_align.setProperty(name, value) for name, value in kwargs.items() ] try: algorithm_align.execute() except Exception as err: if soft_crash is True: logger.error('Execution continues') else: raise err.__class__(err) logger.notice(f'{name} has executed')
def live_monitor(self, input_ws, output_ws): """ :return: """ # Now get the data, read the first spectra spectra = input_ws.readY(0) # extract the first value from the array count = spectra[0] print(f'Total count: {count}') count = input_ws.getNumberEvents() # output it as a log message logger.notice("Total counts so far " + str(count)) # if my ouput workspace has not been created yet, create it. if not mtd.doesExist(output_ws): table = CreateEmptyTableWorkspace(OutputWorkspace=output_ws) table.setTitle("Event Rate History") table.addColumn("str", "Time") table.addColumn('str', 'EventsS') table.addColumn("int", "Events") table = mtd[output_ws] assert table
def action_show_in_explorer(self): widget = self.view.widget(self.view.last_tab_clicked) if not widget: # the widget has been deleted without updating the last tab clicked return filepath = widget.editor.fileName() if "" != filepath: ShowInExplorer.open_directory(os.path.dirname(filepath)) else: logger.notice("Selected tab is not saved to a file, and cannot be shown in explorer.")
def _report_progress(self, msg): """ :param msg: message to print out """ # In order to avoid # # RuntimeError: Pickling of "mantid.kernel._kernel.Logger" # instances is not enabled (http://www.boost.org/libs/python/doc/v2/pickle.html) # # logger has to be imported locally from mantid.kernel import logger logger.notice(msg)
def action_show_in_explorer(self): widget = self.view.widget(self.view.last_tab_clicked) if not widget: # the widget has been deleted without updating the last tab clicked return filepath = widget.editor.fileName() if "" != filepath: ShowInExplorer.open_directory(os.path.dirname(filepath)) else: logger.notice( "Selected tab is not saved to a file, and cannot be shown in explorer." )
def _logSuccessExport(self, wsName): """ Log all the successful exports. Args: wsName (str): name of the concerned workspace """ if wsName not in self._successExports: return filenames = ", ".join(self._successExports[wsName]) logger.notice("Successful export of workspace {} to {}" .format(wsName, filenames)) del self._successExports[wsName]
def read_vibrational_or_phonon_data(self): """ Reads vibrational or phonon data from CRYSTAL output files. Saves frequencies, weights of k-point vectors, k-point vectors, amplitudes of atomic displacements, hash of the vibrational or phonon data file (hash) to <>.hdf5. :return object of type AbinsData. """ # determine system (molecule or crystal?) system = self._determine_system() # check if one or more k-points to parse phonon_dispersion = self._determine_dispersion() # read data from output CRYSTAL file filename = self._clerk.get_input_filename() with io.open(filename, "rb") as crystal_file: logger.notice("Reading from " + filename) if system is AbinsModules.AbinsConstants.CRYSTAL: lattice_vectors = self._read_lattice_vectors( file_obj=crystal_file) else: lattice_vectors = [[0, 0, 0]] * 3 coord_lines = self._read_atomic_coordinates(file_obj=crystal_file) masses = self._read_masses_from_file(file_obj=crystal_file) freq, coordinates, weights, k_coordinates = self._read_modes( file_obj=crystal_file, phonon_dispersion=phonon_dispersion) # put data into Abins data structure data = {} self._create_atoms_data(data=data, coord_lines=coord_lines[:self._num_atoms], atoms_masses=masses[:self._num_atoms]) self._create_kpoints_data( data=data, freq=freq, atomic_displacements=coordinates, atomic_coordinates=coord_lines[:self._num_atoms], weights=weights, k_coordinates=k_coordinates, unit_cell=lattice_vectors) # save data to hdf file self.save_ab_initio_data(data=data) # return AbinsData object return self._rearrange_data(data=data)
def polCorr(polcorr, IvsLam, crho, calpha, cAp, cPp): ''' Perform polynomial correction ''' # Treat False like a polarization correction of None. if polcorr == PolarisationCorrection.PNR: IvsLam = nrPNRCorrection(IvsLam.name(), crho[0],calpha[0],cAp[0],cPp[0]) elif polcorr == PolarisationCorrection.PA: IvsLam = nrPACorrection(IvsLam.name(), crho[0],calpha[0],cAp[0],cPp[0]) else: message = "No Polarisation Correction Requested." logger.notice(message) print message return IvsLam
def _normalize_one_spectrum(single_spectrum_ws, spline, instrument): rebinned_spline = mantid.RebinToWorkspace( WorkspaceToRebin=spline, WorkspaceToMatch=single_spectrum_ws, StoreInADS=False) divided = mantid.Divide(LHSWorkspace=single_spectrum_ws, RHSWorkspace=rebinned_spline, StoreInADS=False) if instrument.get_instrument_prefix() == "GEM": values_replaced = mantid.ReplaceSpecialValues(InputWorkspace=divided, NaNValue=0, StoreInADS=False) # crop based off max between 1000 and 2000 tof as the vanadium peak on Gem will always occur here complete = _crop_spline_to_percent_of_max(rebinned_spline, values_replaced, single_spectrum_ws, 1000, 2000) else: complete = mantid.ReplaceSpecialValues( InputWorkspace=divided, NaNValue=0, OutputWorkspace=single_spectrum_ws) if instrument.perform_abs_vanadium_norm(): vanadium_material = spline.sample().getMaterial() v_number_density = vanadium_material.numberDensityEffective v_cross_section = vanadium_material.totalScatterXSection() vanadium_shape = spline.sample().getShape() # number density in Angstroms-3, volume in m3. Don't bother with 1E30 factor because will cancel num_v_atoms = vanadium_shape.volume() * v_number_density sample_material = single_spectrum_ws.sample().getMaterial() sample_number_density = sample_material.numberDensityEffective sample_shape = spline.sample().getShape() num_sample_atoms = sample_shape.volume() * sample_number_density abs_norm_factor = v_cross_section * num_v_atoms / \ (num_sample_atoms * 4 * math.pi) logger.notice( "Performing absolute normalisation, multiplying by factor=" + str(abs_norm_factor)) # avoid "Variable invalidated, data has been deleted" error when debugging output_ws_name = single_spectrum_ws.name() abs_norm_factor_ws = mantid.CreateSingleValuedWorkspace( DataValue=abs_norm_factor, OutputWorkspace="__abs_norm_factor_ws") complete = mantid.Multiply(LHSWorkspace=complete, RHSWorkspace=abs_norm_factor_ws, OutputWorkspace=output_ws_name) return complete
def create_output_files(self, calibration_dir, difa, difc, tzero, sample_path, vanadium_path, instrument, bank, spectrum_numbers): """ Create output files from the algorithms in the specified directory :param calibration_dir: The directory to save the files into. :param difa: DIFA values from calibration algorithm :param difc: DIFC values from the calibration algorithm. :param tzero: TZERO values from the calibration algorithm. :param sample_path: The path to the sample data file. :param vanadium_path: The path to the vanadium data file. :param instrument: The instrument (ENGINX or IMAT) :param bank: Optional parameter to crop by bank :param spectrum_numbers: Optional parameter to crop using spectrum numbers. """ kwargs = {"ceria_run": path_handling.get_run_number_from_path(sample_path, instrument), "vanadium_run": path_handling.get_run_number_from_path(vanadium_path, instrument)} def south_kwargs(): kwargs["template_file"] = SOUTH_BANK_TEMPLATE_FILE kwargs["bank_names"] = ["South"] def north_kwargs(): kwargs["template_file"] = NORTH_BANK_TEMPLATE_FILE kwargs["bank_names"] = ["North"] def generate_output_file(difa_list, difc_list, tzero_list, bank_name, kwargs_to_pass): file_path = calibration_dir + self._generate_output_file_name(vanadium_path, sample_path, instrument, bank=bank_name) write_ENGINX_GSAS_iparam_file(file_path, difa_list, difc_list, tzero_list, **kwargs_to_pass) if not path.exists(calibration_dir): makedirs(calibration_dir) if bank is None and spectrum_numbers is None: generate_output_file(difa, difc, tzero, "all", kwargs) north_kwargs() generate_output_file([difa[0]], [difc[0]], [tzero[0]], "north", kwargs) south_kwargs() generate_output_file([difa[1]], [difc[1]], [tzero[1]], "south", kwargs) elif bank == "1": north_kwargs() generate_output_file([difa[0]], [difc[0]], [tzero[0]], "north", kwargs) elif bank == "2": south_kwargs() generate_output_file([difa[0]], [difc[0]], [tzero[0]], "south", kwargs) elif bank is None: # Custom cropped files use the north bank template. north_kwargs() generate_output_file([difa[0]], [difc[0]], [tzero[0]], "cropped", kwargs) logger.notice(f"\n\nCalibration files saved to: \"{calibration_dir}\"\n\n")
def _create_and_save_configuration(self): logger.notice('Reduction will not be carried out') # # configuration file name and save location # basename = datetime.now().strftime('%Y-%m-%d_%H-%M-%S') dir_name = self.getProperty('ConfigSaveDir').value if len(dir_name) <= 0: # default directory run_number = self.getProperty('RunNumbers').value[ 0] # first run number dir_name = Path(self.get_IPTS_Local( run_number)) / 'shared' / 'autoreduce' / 'configurations' dir_name.mkdir( parents=True, exist_ok=True) # in case it has not yet been created filename = str(Path(dir_name) / f'{basename}.json') # # Selected algorithm properties as a dictionary # dict_repr = json.loads(str(self)).get( 'properties' ) # representation of the algorithm's properties in a dict # Remove not wanted properties for not_wanted in ('RunNumbers', 'OutputDirectory', 'EnableConfigurator', 'ConfigSaveDir'): if not_wanted in dict_repr: del dict_repr[not_wanted] """ hack to fix the entry for the default JSON represenation of property DetCalFilename, which is saved as a list of lists Example: "DetCalFilename": [ ["/SNS/SNAP/IPTS-26217/shared/E76p2_W65p3.detcal"], ["/SNS/SNAP/IPTS-26217/shared/E76p2_W65p5.detcal"]] must become: "DetCalFilename": "/SNS/SNAP/IPTS-26217/shared/E76p2_W65p3.detcal,/SNS/SNAP/IPTS-26217/shared/E76p2_W65p5.detcal" """ if 'DetCalFilename' in dict_repr: dict_repr['DetCalFilename'] = ','.join( [entry[0] for entry in dict_repr.get('DetCalFilename')]) # # Save to file in JSON format # formatted_pretty = json.dumps(dict_repr, sort_keys=True, indent=4) with open(filename, 'w') as f: f.write(formatted_pretty) logger.information(f'Saving configuration to {filename}') logger.debug(f'Configuration contents:\n{formatted_pretty}')
def PyExec(self): """ Main execution body """ #get parameters w = self.getProperty("Workspace").value logNames = self.getProperty("LogNames").value resultString = '' #check for parameters and build the result string for value in logNames.split(','): value = value.strip() if len(value) > 0: if not w.run().hasProperty(value): resultString += 'Property ' + value + ' not found\n' #return the result logger.notice(resultString) self.setProperty("Result", resultString) return
def open_directory(path): try: if sys.platform == "win32": os.startfile(path) elif sys.platform == 'darwin': subprocess.check_call(['open', '--', path]) elif sys.platform == 'linux2': call_params = ['xdg-open', path] subprocess.check_call(call_params) except Exception as ex: # it is hard to narrow down the type of exception, as too many things can go wrong: # - subprocess.CalledProcessError if the process has an error # - OSError if the command cannot be found (on Linux) # - WindowsError is the directory does not exist # However, catching this general exception makes sure that there will not be a crash # regardless of what the error is logger.notice("Could not open the folder in explorer.") logger.debug("Error encountered: {}".format(ex))
def PyExec(self): """ Main execution body """ #get parameters w = self.getProperty("Workspace").value logNames = self.getProperty("LogNames").value resultString='' #check for parameters and build the result string for value in logNames.split(','): value=value.strip() if len(value)>0: if not w.run().hasProperty(value): resultString+='Property '+value+' not found\n' #return the result logger.notice(resultString) self.setProperty("Result",resultString) return
def euphonic_calculate_modes(filename: str, cutoff: float = 20., gamma: bool = True, acoustic_sum_rule: Optional[str] = 'reciprocal'): """ Read force constants file with Euphonic and sample frequencies/modes :param filename: Input data :param cutoff: Sampling density of Brillouin-zone. Specified as real-space length cutoff in Angstrom. :param gamma: Shift sampling grid to include the Gamma-point. :param acoustic_sum_rule: Apply acoustic sum rule correction to force constants: options are 'realspace' and 'reciprocal', specifying different implementations of the correction. If None, no correction is applied. This option is referred to as "asr" in the Euphonic python API and command-line tools. :returns: euphonic.QpointPhononModes """ from math import ceil from euphonic.cli.utils import force_constants_from_file from euphonic.util import mp_grid fc = force_constants_from_file(filename) recip_lattice_lengths = np.linalg.norm( fc.crystal.reciprocal_cell().to('1/angstrom').magnitude, axis=1) mp_sampling = [ceil(x) for x in (cutoff * recip_lattice_lengths / (2 * np.pi))] qpts = mp_grid(mp_sampling) if gamma: mp_sampling = np.array(mp_sampling, dtype=int) # Shift directions with even number of samples by half the grid spacing offsets = ((mp_sampling + 1) % 2) * (0.5 / mp_sampling) qpts += offsets logger.notice('Calculating phonon modes on {} grid'.format( 'x'.join(map(str, mp_sampling)))) modes = fc.calculate_qpoint_phonon_modes(qpts, asr=acoustic_sum_rule) return modes
def read_vibrational_or_phonon_data(self): """ Reads vibrational or phonon data from CRYSTAL output files. Saves frequencies, weights of k-point vectors, k-point vectors, amplitudes of atomic displacements, hash of the vibrational or phonon data file (hash) to <>.hdf5. :return object of type AbinsData. """ # determine system (molecule or crystal?) system = self._determine_system() # check if one or more k-points to parse phonon_dispersion = self._determine_dispersion() # read data from output CRYSTAL file filename = self._clerk.get_input_filename() with io.open(filename, "rb") as crystal_file: logger.notice("Reading from " + filename) if system is AbinsModules.AbinsConstants.CRYSTAL: lattice_vectors = self._read_lattice_vectors(file_obj=crystal_file) else: lattice_vectors = [[0, 0, 0]] * 3 coord_lines = self._read_atomic_coordinates(file_obj=crystal_file) masses = self._read_masses_from_file(file_obj=crystal_file) freq, coordinates, weights, k_coordinates = self._read_modes(file_obj=crystal_file, phonon_dispersion=phonon_dispersion) # put data into Abins data structure data = {} self._create_atoms_data(data=data, coord_lines=coord_lines[:self._num_atoms], atoms_masses=masses[:self._num_atoms]) self._create_kpoints_data(data=data, freq=freq, atomic_displacements=coordinates, atomic_coordinates=coord_lines[:self._num_atoms], weights=weights, k_coordinates=k_coordinates, unit_cell=lattice_vectors) # save data to hdf file self.save_ab_initio_data(data=data) # return AbinsData object return self._rearrange_data(data=data)
def get_bank_grouping_workspace(bank: int, sample_raw): # -> GroupingWorkspace """ Retrieve the grouping workspace for the North/South bank from the user directories, or create a new one from the sample workspace instrument data if not found :param bank: integer denoting the bank, 1 or 2 for North/South respectively :param sample_raw: Workspace containing the instrument data that can be used to create a new grouping workspace :return: The loaded or created grouping workspace """ if bank == 1: try: if ADS.doesExist("NorthBank_grouping"): return ADS.retrieve("NorthBank_grouping") grp_ws = mantid.LoadDetectorsGroupingFile( InputFile="ENGINX_North_grouping.xml", OutputWorkspace="NorthBank_grouping") return grp_ws except ValueError: logger.notice( "NorthBank grouping file not found in user directories - creating one" ) bank_name = "NorthBank" elif bank == 2: try: if ADS.doesExist("SouthBank_grouping"): return ADS.retrieve("SouthBank_grouping") grp_ws = mantid.LoadDetectorsGroupingFile( InputFile="ENGINX_South_grouping.xml", OutputWorkspace="SouthBank_grouping") return grp_ws except ValueError: logger.notice( "SouthBank grouping file not found in user directories - creating one" ) bank_name = "SouthBank" else: raise ValueError("Invalid bank number given") ws_name = bank_name + "_grouping" grp_ws, _, _ = mantid.CreateGroupingWorkspace(InputWorkspace=sample_raw, GroupNames=bank_name, OutputWorkspace=ws_name) return grp_ws
def _determine_system(self): """ Determines whether the system is a molecule or a crystal. :returns: True if calculation for molecule otherwise False """ with io.open(self._clerk.get_input_filename(), "rb") as crystal_file: lines = crystal_file.read() if b"MOLECULAR CALCULATION" in lines or b"0D - MOLECULE" in lines: molecular = True elif b"CRYSTAL CALCULATION" in lines or b"3D - CRYSTAL" in lines: molecular = False else: raise ValueError("Only molecular or 3D CRYSTAL systems can be processed") if molecular: logger.notice("This run is for a MOLECULAR system") else: logger.notice("This run is for a 3D CRYSTAL system") return molecular
def create_output_files(calibration_dir, calibration, ws_foc): """ Create output files (.prm for GSAS and .nxs of calibration table) from the algorithms in the specified directory :param calibration_dir: The directory to save the files into. :param calibration: CalibrationInfo object with details of calibration and grouping :param ws_foc: focused ceria workspace """ # create calibration dir of not exist if not path.exists(calibration_dir): makedirs(calibration_dir) # save grouping ws if custom or cropped if not calibration.group.banks: calibration.save_grouping_workspace(calibration_dir) # save prm file(s) prm_filepath = path.join(calibration_dir, calibration.generate_output_file_name()) write_prm_file(ws_foc, prm_filepath) calibration.set_prm_filepath(prm_filepath) # save pdcal output as nexus filepath, ext = path.splitext(prm_filepath) nxs_filepath = filepath + '.nxs' mantid.SaveNexus(InputWorkspace=calibration.get_calibration_table(), Filename=nxs_filepath) # if both banks calibrated save individual banks separately as well if calibration.group == GROUP.BOTH: # output a separate prm for North and South when both banks included for ibank, bank in enumerate(calibration.group.banks): # get prm filename for individual banks by passing group enum as argument to generate_output_file_name prm_filepath_bank = path.join( calibration_dir, calibration.generate_output_file_name(GROUP(str(ibank + 1)))) write_prm_file(ws_foc, prm_filepath_bank, spec_nums=[ibank]) # copy pdcal output nxs for both banks filepath, ext = path.splitext(prm_filepath_bank) nxs_filepath_bank = filepath + '.nxs' copy2(nxs_filepath, nxs_filepath_bank) logger.notice(f"\n\nCalibration files saved to: \"{calibration_dir}\"\n\n")
def get_formatted_data(self): # try to load DFT data from *.hdf5 file try: if self._dft_program != self._clerk.get_previous_dft_program(): raise ValueError( "Different DFT program was used in the previous calculation. Data in the hdf file " "will be erased.") self._clerk.check_previous_data() dft_data = self.load_formatted_data() logger.notice( str(dft_data) + " has been loaded from the HDF file.") # if loading from *.hdf5 file failed than read data directly from input DFT file and erase hdf file except (IOError, ValueError) as err: logger.notice(str(err)) self._clerk.erase_hdf_file() dft_data = self.read_phonon_file() logger.notice( str(dft_data) + " from DFT input file has been loaded.") return dft_data
def get_formatted_data(self): # try to load ab initio data from *.hdf5 file try: if self._ab_initio_program != self._clerk.get_previous_ab_initio_program( ): raise ValueError( "Different ab initio program was used in the previous calculation. Data in the hdf " "file will be erased.") self._clerk.check_previous_data() ab_initio_data = self.load_formatted_data() logger.notice( str(ab_initio_data) + " has been loaded from the HDF file.") # if loading from *.hdf5 file failed than read data directly from input ab initio file and erase hdf file except (IOError, ValueError) as err: logger.notice(str(err)) self._clerk.erase_hdf_file() ab_initio_data = self.read_vibrational_or_phonon_data() logger.notice( str(ab_initio_data) + " from ab initio input file has been loaded.") return ab_initio_data
def create_bank_grouping_workspace(self): """ Create grouping workspace for ROI corresponding to one or more banks """ ws_name = GROUP_WS_NAMES[self.group] grp_ws = None try: grp_ws = LoadDetectorsGroupingFile(InputFile=path.join( CALIB_DIR, GROUP_FILES[self.group]), OutputWorkspace=ws_name) except ValueError: logger.notice( "Grouping file not found in user directories - creating one") if self.group.banks and self.group != GROUP.TEXTURE20 and self.group != GROUP.TEXTURE30: grp_ws, _, _ = CreateGroupingWorkspace( InstrumentName=self.instrument, OutputWorkspace=ws_name, GroupNames=GROUP_BANK_ARGS[self.group]) if grp_ws: self.group_ws = grp_ws else: raise ValueError( "Could not find or create grouping requested - make sure the directory of the grouping.xml" " files is on the path")
def load_bank_table(bank_id: int, database_path: str, date: str, table_type: str = 'calibration') -> TableWorkspace: """ Function that loads the latest bank calibrated TableWorkspace from a single HDF5 file using corelli format: database_path/bank0ID/type_corelli_bank0ID_YYYYMMDD.nxs :param bank_id bank number that is calibrated :param database_path location of the corelli database (absolute or relative) Example: database/corelli/ for database/corelli/bank001/ database/corelli/bank002/ :param date current day in YYYYMMDD format :param table_type 'calibration', 'mask' or 'fit' :return TableWorkspace with corresponding data """ TableType.assert_valid_type(table_type) verify_date_format('load_bank_table', date) filename: str = filename_bank_table(bank_id, database_path, date, table_type) logger.notice(f'Loading bank{bank_id} {table_type} file from database') outputWS = LoadNexusProcessed(filename) return outputWS
def get_formatted_data(self): # try to load ab initio data from *.hdf5 file try: self._clerk.check_previous_data() ab_initio_data = self.load_formatted_data() logger.notice(str(ab_initio_data) + " has been loaded from the HDF file.") # if loading from *.hdf5 file failed than read data directly from input ab initio file and erase hdf file except (IOError, ValueError) as err: logger.notice(str(err)) self._clerk.erase_hdf_file() ab_initio_data = self.read_vibrational_or_phonon_data() logger.notice(str(ab_initio_data) + " from ab initio input file has been loaded.") return ab_initio_data
def get_formatted_data(self): # try to load ab initio data from *.hdf5 file try: if self._ab_initio_program != self._clerk.get_previous_ab_initio_program(): raise ValueError("Different ab initio program was used in the previous calculation. Data in the hdf " "file will be erased.") self._clerk.check_previous_data() ab_initio_data = self.load_formatted_data() logger.notice(str(ab_initio_data) + " has been loaded from the HDF file.") # if loading from *.hdf5 file failed than read data directly from input ab initio file and erase hdf file except (IOError, ValueError) as err: logger.notice(str(err)) self._clerk.erase_hdf_file() ab_initio_data = self.read_vibrational_or_phonon_data() logger.notice(str(ab_initio_data) + " from ab initio input file has been loaded.") return ab_initio_data
def make_trans_corr(transrun, stitch_start_overlap, stitch_end_overlap, stitch_params, lambda_min=None, lambda_max=None, background_min=None, background_max=None, int_min=None, int_max=None, detector_index_ranges=None, i0_monitor_index=None): ''' Make the transmission correction workspace. ''' ''' Check to see whether all optional inputs have been provide. If not we have to get them from the IDF. ''' if not all((lambda_min, lambda_max, background_min, background_max, int_min, int_max, detector_index_ranges, i0_monitor_index)): logger.notice("make_trans_corr: Fetching missing arguments from the IDF") instrument_source = transrun if isinstance(transrun, str): instrument_source = transrun.split(',')[0] trans_ws = ConvertToWavelength.to_workspace(instrument_source) idf_defaults = get_defaults(trans_ws) # Fetch defaults for anything not specified if not i0_monitor_index: i0_monitor_index = idf_defaults['I0MonitorIndex'] if not lambda_min: lambda_min = idf_defaults['LambdaMin'] if not lambda_max: lambda_max = idf_defaults['LambdaMax'] if not detector_index_ranges: point_detector_start = idf_defaults['PointDetectorStart'] point_detector_stop = idf_defaults['PointDetectorStop'] detector_index_ranges = (point_detector_start, point_detector_stop) if not background_min: background_min = idf_defaults['MonitorBackgroundMin'] if not background_max: background_max = idf_defaults['MonitorBackgroundMax'] if not int_min: int_min = idf_defaults['MonitorIntegralMin'] if not int_max: int_max = idf_defaults['MonitorIntegralMax'] transWS = None if isinstance(transrun, str) and (',' in transrun): slam = transrun.split(',')[0] llam = transrun.split(',')[1] print "Transmission runs: ", transrun to_lam = ConvertToWavelength(slam) _monitor_ws_slam, _detector_ws_slam = to_lam.convert(wavelength_min=lambda_min, wavelength_max=lambda_max, detector_workspace_indexes=detector_index_ranges, monitor_workspace_index=i0_monitor_index, correct_monitor=True, bg_min=background_min, bg_max=background_max ) _i0p_slam = RebinToWorkspace(WorkspaceToRebin=_monitor_ws_slam, WorkspaceToMatch=_detector_ws_slam) _mon_int_trans = Integration(InputWorkspace=_i0p_slam, RangeLower=int_min, RangeUpper=int_max) _detector_ws_slam = Divide(LHSWorkspace=_detector_ws_slam, RHSWorkspace=_mon_int_trans) to_lam = ConvertToWavelength(llam) _monitor_ws_llam, _detector_ws_llam = to_lam.convert(wavelength_min=lambda_min, wavelength_max=lambda_max, detector_workspace_indexes=detector_index_ranges, monitor_workspace_index=i0_monitor_index, correct_monitor=True, bg_min=background_min, bg_max=background_max ) _i0p_llam = RebinToWorkspace(WorkspaceToRebin=_monitor_ws_llam, WorkspaceToMatch=_detector_ws_llam) _mon_int_trans = Integration(InputWorkspace=_i0p_llam, RangeLower=int_min,RangeUpper=int_max) _detector_ws_llam = Divide(LHSWorkspace=_detector_ws_llam, RHSWorkspace=_mon_int_trans) print stitch_start_overlap, stitch_end_overlap, stitch_params transWS, outputScaling = Stitch1D(LHSWorkspace=_detector_ws_slam, RHSWorkspace=_detector_ws_llam, StartOverlap=stitch_start_overlap, EndOverlap=stitch_end_overlap, Params=stitch_params) transWS = RenameWorkspace(InputWorkspace=transWS, OutputWorkspace="TRANS_" + slam + "_" + llam) else: to_lam = ConvertToWavelength(transrun) _monitor_ws_trans, _detector_ws_trans = to_lam.convert(wavelength_min=lambda_min, wavelength_max=lambda_max, detector_workspace_indexes=detector_index_ranges, monitor_workspace_index=i0_monitor_index, correct_monitor=True, bg_min=background_min, bg_max=background_max ) _i0p_trans = RebinToWorkspace(WorkspaceToRebin=_monitor_ws_trans, WorkspaceToMatch=_detector_ws_trans) _mon_int_trans = Integration( InputWorkspace=_i0p_trans, RangeLower=int_min, RangeUpper=int_max ) transWS = Divide( LHSWorkspace=_detector_ws_trans, RHSWorkspace=_mon_int_trans ) transWS = RenameWorkspace(InputWorkspace=transWS, OutputWorkspace="TRANS_" + transrun) return transWS
def nrPNRCorrection(Wksp,crho,calpha,cAp,cPp): # Constants Based on Runs 18350+18355 and 18351+18356 analyser theta at -0.1deg # 2 RF Flippers as the polarising system # crho=[1.006831,-0.011467,0.002244,-0.000095] # calpha=[1.017526,-0.017183,0.003136,-0.000140] # cAp=[0.917940,0.038265,-0.006645,0.000282] # cPp=[0.972762,0.001828,-0.000261,0.0] message = "Performing PNR correction" logger.notice(message) print message CloneWorkspace(Wksp,OutputWorkspace='_'+Wksp+'_uncorrected') if ( (not isinstance(mtd[Wksp], WorkspaceGroup)) or (not mtd[Wksp].size()==2) ): print "PNR correction works only with exactly 2 periods!" return mtd[Wksp] else: Ip = mtd[Wksp][0] Ia = mtd[Wksp][1] CloneWorkspace(Ip,OutputWorkspace="PCalpha") CropWorkspace(InputWorkspace="PCalpha",OutputWorkspace="PCalpha",StartWorkspaceIndex="0",EndWorkspaceIndex="0") PCalpha=(mtd['PCalpha']*0.0)+1.0 alpha=mtd['PCalpha'] # a1=alpha.readY(0) # for i in range(0,len(a1)): # alpha.dataY(0)[i]=0.0 # alpha.dataE(0)[i]=0.0 CloneWorkspace("PCalpha",OutputWorkspace="PCrho") CloneWorkspace("PCalpha",OutputWorkspace="PCAp") CloneWorkspace("PCalpha",OutputWorkspace="PCPp") rho=mtd['PCrho'] Ap=mtd['PCAp'] Pp=mtd['PCPp'] # for i in range(0,len(a1)): # x=(alpha.dataX(0)[i]+alpha.dataX(0)[i])/2.0 # for j in range(0,4): # alpha.dataY(0)[i]=alpha.dataY(0)[i]+calpha[j]*x**j # rho.dataY(0)[i]=rho.dataY(0)[i]+crho[j]*x**j # Ap.dataY(0)[i]=Ap.dataY(0)[i]+cAp[j]*x**j # Pp.dataY(0)[i]=Pp.dataY(0)[i]+cPp[j]*x**j PolynomialCorrection(InputWorkspace="PCalpha",OutputWorkspace="PCalpha",Coefficients=calpha,Operation="Multiply") PolynomialCorrection(InputWorkspace="PCrho",OutputWorkspace="PCrho",Coefficients=crho,Operation="Multiply") PolynomialCorrection(InputWorkspace="PCAp",OutputWorkspace="PCAp",Coefficients=cAp,Operation="Multiply") PolynomialCorrection(InputWorkspace="PCPp",OutputWorkspace="PCPp",Coefficients=cPp,Operation="Multiply") D=Pp*(1.0+rho) nIp=(Ip*(rho*Pp+1.0)+Ia*(Pp-1.0))/D nIa=(Ip*(rho*Pp-1.0)+Ia*(Pp+1.0))/D RenameWorkspace(nIp,OutputWorkspace=str(Ip)+"corr") RenameWorkspace(nIa,OutputWorkspace=str(Ia)+"corr") out = GroupWorkspaces(str(Ip)+"corr"+','+str(Ia)+"corr",OutputWorkspace=Wksp) #CloneWorkspace=(InputWorkspace="gr",OutputWorkspace=Wksp) iwksp = mtd.getObjectNames() list=[str(Ip),str(Ia),"PCalpha","PCrho","PCAp","PCPp","1_p"] for i in range(len(iwksp)): for j in list: lname=len(j) if iwksp[i] [0:lname+1] == j+"_": DeleteWorkspace(iwksp[i]) DeleteWorkspace("PCalpha") DeleteWorkspace("PCrho") DeleteWorkspace("PCAp") DeleteWorkspace("PCPp") DeleteWorkspace("D") return out
def nrPACorrection(Wksp,crho,calpha,cAp,cPp):#UpUpWksp,UpDownWksp,DownUpWksp,DownDownWksp): # crho=[0.941893,0.0234006,-0.00210536,0.0] # calpha=[0.945088,0.0242861,-0.00213624,0.0] # cAp=[1.00079,-0.0186778,0.00131546,0.0] # cPp=[1.01649,-0.0228172,0.00214626,0.0] # Constants Based on Runs 18350+18355 and 18351+18356 analyser theta at -0.1deg # 2 RF Flippers as the polarising system # Ipa and Iap appear to be swapped in the sequence on CRISP 4 perido data! message = "Performing PA correction" logger.notice(message) print message CloneWorkspace(Wksp,OutputWorkspace='_'+Wksp+'_uncorrected') if (not isinstance(mtd[Wksp], WorkspaceGroup)) or (not mtd[Wksp].size()==4) : print "PNR correction works only with exactly 4 periods (uu,ud,du,dd)!" return mtd[Wksp] else: Ipp = mtd[Wksp][0] Ipa = mtd[Wksp][1] Iap = mtd[Wksp][2] Iaa = mtd[Wksp][3] CloneWorkspace(Ipp,OutputWorkspace="PCalpha") CropWorkspace(InputWorkspace="PCalpha",OutputWorkspace="PCalpha",StartWorkspaceIndex=0,EndWorkspaceIndex=0) PCalpha=(mtd['PCalpha']*0.0)+1.0 alpha=mtd['PCalpha'] CloneWorkspace("PCalpha",OutputWorkspace="PCrho") CloneWorkspace("PCalpha",OutputWorkspace="PCAp") CloneWorkspace("PCalpha",OutputWorkspace="PCPp") rho=mtd['PCrho'] Ap=mtd['PCAp'] Pp=mtd['PCPp'] # Use the polynomial corretion fn instead PolynomialCorrection(InputWorkspace="PCalpha",OutputWorkspace="PCalpha",Coefficients=calpha,Operation="Multiply") PolynomialCorrection(InputWorkspace="PCrho",OutputWorkspace="PCrho",Coefficients=crho,Operation="Multiply") PolynomialCorrection(InputWorkspace="PCAp",OutputWorkspace="PCAp",Coefficients=cAp,Operation="Multiply") PolynomialCorrection(InputWorkspace="PCPp",OutputWorkspace="PCPp",Coefficients=cPp,Operation="Multiply") A0 = (Iaa * Pp * Ap) + (Ap * Ipa * rho * Pp) + (Ap * Iap * Pp * alpha) + (Ipp * Ap * alpha * rho * Pp) A1 = Pp * Iaa A2 = Pp * Iap A3 = Ap * Iaa A4 = Ap * Ipa A5 = Ap * alpha * Ipp A6 = Ap * alpha * Iap A7 = Pp * rho * Ipp A8 = Pp * rho * Ipa D = Pp * Ap *( 1.0 + rho + alpha + (rho * alpha) ) nIpp = (A0 - A1 + A2 - A3 + A4 + A5 - A6 + A7 - A8 + Ipp + Iaa - Ipa - Iap) / D nIaa = (A0 + A1 - A2 + A3 - A4 - A5 + A6 - A7 + A8 + Ipp + Iaa - Ipa - Iap) / D nIpa = (A0 - A1 + A2 + A3 - A4 - A5 + A6 + A7 - A8 - Ipp - Iaa + Ipa + Iap) / D nIap = (A0 + A1 - A2 - A3 + A4 + A5 - A6 - A7 + A8 - Ipp - Iaa + Ipa + Iap) / D ipp_corr = RenameWorkspace(nIpp,OutputWorkspace=str(Ipp)+"corr") ipa_corr = RenameWorkspace(nIpa,OutputWorkspace=str(Ipa)+"corr") iap_corr = RenameWorkspace(nIap,OutputWorkspace=str(Iap)+"corr") iaa_corr = RenameWorkspace(nIaa,OutputWorkspace=str(Iaa)+"corr") ReplaceSpecialValues(str(Ipp)+"corr",OutputWorkspace=str(Ipp)+"corr",NaNValue="0.0",NaNError="0.0",InfinityValue="0.0",InfinityError="0.0") ReplaceSpecialValues(str(Ipp)+"corr",OutputWorkspace=str(Ipp)+"corr",NaNValue="0.0",NaNError="0.0",InfinityValue="0.0",InfinityError="0.0") ReplaceSpecialValues(str(Ipp)+"corr",OutputWorkspace=str(Ipp)+"corr",NaNValue="0.0",NaNError="0.0",InfinityValue="0.0",InfinityError="0.0") ReplaceSpecialValues(str(Ipp)+"corr",OutputWorkspace=str(Ipp)+"corr",NaNValue="0.0",NaNError="0.0",InfinityValue="0.0",InfinityError="0.0") iwksp=mtd.getObjectNames() list=[str(Ipp),str(Ipa),str(Iap),str(Iaa),"PCalpha","PCrho","PCAp","PCPp","1_p"] for i in range(len(iwksp)): for j in list: lname=len(j) if iwksp[i] [0:lname+1] == j+"_": DeleteWorkspace(iwksp[i]) DeleteWorkspace("PCalpha") DeleteWorkspace("PCrho") DeleteWorkspace("PCAp") DeleteWorkspace("PCPp") DeleteWorkspace('A0') DeleteWorkspace('A1') DeleteWorkspace('A2') DeleteWorkspace('A3') DeleteWorkspace('A4') DeleteWorkspace('A5') DeleteWorkspace('A6') DeleteWorkspace('A7') DeleteWorkspace('A8') DeleteWorkspace('D') out = GroupWorkspaces("%s, %s, %s, %s" % (ipp_corr.name(), ipa_corr.name(), iap_corr.name(), iaa_corr.name())) return out
def generate_plot_script_clipboard(self): script = generate_script(self.canvas.figure, exclude_headers=True) QApplication.clipboard().setText(script) logger.notice("Plotting script copied to clipboard.")
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 create_output_files(self, calibration_dir, difa, difc, tzero, bk2bk_params, ceria_path, instrument, bank, spectrum_numbers, calfile): """ Create output files from the algorithms in the specified directory :param calibration_dir: The directory to save the files into. :param difa: DIFA values from calibration algorithm. :param difc: DIFC values from the calibration algorithm. :param tzero: TZERO values from the calibration algorithm. :param bk2bk_params: BackToBackExponential parameters from Parameters.xml file. :param ceria_path: The path to the ceria data file. :param instrument: The instrument (ENGINX or IMAT). :param bank: Optional parameter to crop by bank. :param spectrum_numbers: Optional parameter to crop using spectrum numbers. :param calfile: Optional parameter to crop with a custom calfile """ kwargs = { "ceria_run": path_handling.get_run_number_from_path(ceria_path, instrument) } def south_kwargs(): kwargs["template_file"] = SOUTH_BANK_TEMPLATE_FILE kwargs["bank_names"] = ["South"] def north_kwargs(): kwargs["template_file"] = NORTH_BANK_TEMPLATE_FILE kwargs["bank_names"] = ["North"] def generate_prm_output_file(difa_list, difc_list, tzero_list, bank_name, kwargs_to_pass): file_path = calibration_dir + EnggUtils.generate_output_file_name( ceria_path, instrument, bank=bank_name) EnggUtils.write_ENGINX_GSAS_iparam_file(file_path, difa_list, difc_list, tzero_list, bk2bk_params, **kwargs_to_pass) set_setting(output_settings.INTERFACES_SETTINGS_GROUP, output_settings.ENGINEERING_PREFIX, "last_calibration_path", file_path) def save_pdcal_output_file(ws_name_suffix, bank_name): file_path = calibration_dir + EnggUtils.generate_output_file_name( ceria_path, instrument, bank=bank_name, ext=".nxs") ws_name = "engggui_calibration_" + ws_name_suffix SaveNexus(InputWorkspace=ws_name, Filename=file_path) if not path.exists(calibration_dir): makedirs(calibration_dir) if not (bank or spectrum_numbers or calfile): # both banks generate_prm_output_file(difa, difc, tzero, "all", kwargs) north_kwargs() generate_prm_output_file([difa[0]], [difc[0]], [tzero[0]], "north", kwargs) save_pdcal_output_file("bank_1", "north") south_kwargs() generate_prm_output_file([difa[1]], [difc[1]], [tzero[1]], "south", kwargs) save_pdcal_output_file("bank_2", "south") elif bank == "1": north_kwargs() generate_prm_output_file([difa[0]], [difc[0]], [tzero[0]], "north", kwargs) save_pdcal_output_file("bank_1", "north") elif bank == "2": south_kwargs() generate_prm_output_file([difa[0]], [difc[0]], [tzero[0]], "south", kwargs) save_pdcal_output_file("bank_2", "south") elif spectrum_numbers: # Custom crops use the north bank template north_kwargs() generate_prm_output_file([difa[0]], [difc[0]], [tzero[0]], "Cropped", kwargs) save_pdcal_output_file("Cropped", "Cropped") else: # custom calfile north_kwargs() generate_prm_output_file([difa[0]], [difc[0]], [tzero[0]], "Custom", kwargs) save_pdcal_output_file("Custom", "Custom") logger.notice( f"\n\nCalibration files saved to: \"{calibration_dir}\"\n\n")
def export_masks(ws,fileName='',returnMasksOnly=False): """Exports masks applied to Mantid workspace (e.g. drawn using the instrument view) and write these masks into the old fashioned ASCII .msk file containing masked spectra numbers. The file is Libisis/Mantid old ISIS format compatible and can be read by Libisis or Mantid LoadMasks algorithm If optional parameter fileName is present, the masks are saved in the file with this name Otherwise, the file with the name equal to the workspace name and the extension .msk is used. If returnMasks is set to True, the function does not write to file but returns list of masks instead. """ # get pointer to the workspace if isinstance(ws, str): pws = mtd[ws] else: pws = ws ws_name=pws.getName() nhist = pws.getNumberHistograms() no_detectors = 0 masks = [] for i in range(nhist): # set provisional spectra ID ms = i+1 try: sp = pws.getSpectrum(i) # got real spectra ID, which would correspond real spectra num to spectra ID map ms = sp.getSpectrumNo() #pylint: disable=W0703 except Exception: logger.notice("Can not retrieve spectra No: " + str(i) + ". Have masked it") masks.append(ms) continue try: det = pws.getDetector(i) #pylint: disable=W0703 except Exception: no_detectors = no_detectors +1 masks.append(ms) continue if det.isMasked(): masks.append(ms) filename='' if len(fileName)==0 : filename = os.path.join(config.getString('defaultsave.directory'),ws_name+'.msk') else: filename = fileName nMasks = len(masks) if nMasks == 0: if returnMasksOnly: logger.warning("Workspace {0} have no masked spectra. File {1} have not been created".format(ws_name,filename)) else: logger.notice("Workspace "+ws_name+" have no masked spectra") return masks logger.notice("Workspace {0} has {1} masked spectra, including {2} spectra without detectors".format(ws_name,nMasks,no_detectors)) if not returnMasksOnly : writeISISmasks(filename,masks,8) return masks
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)