def test_determine_best_unit_cell(test_experiments): assert determine_best_unit_cell( test_experiments).parameters() == pytest.approx( test_experiments[0].crystal.get_unit_cell().parameters()) recalc_uc = uctbx.unit_cell((1.1, 1.1, 2.1, 90, 90, 90)) test_experiments[0].crystal.set_recalculated_unit_cell(recalc_uc) assert determine_best_unit_cell( test_experiments).parameters() == pytest.approx(recalc_uc.parameters())
def from_reflections_and_experiments(cls, reflection_tables, experiments, params): """Construct the resolutionizer from native dials datatypes.""" # add some assertions about data # do batch assignment (same functions as in dials.export) offsets = calculate_batch_offsets(experiments) reflection_tables = assign_batches_to_reflections( reflection_tables, offsets) batches = flex.int() intensities = flex.double() indices = flex.miller_index() variances = flex.double() for table in reflection_tables: if "intensity.scale.value" in table: table = filter_reflection_table(table, ["scale"], partiality_threshold=0.4) intensities.extend(table["intensity.scale.value"]) variances.extend(table["intensity.scale.variance"]) else: table = filter_reflection_table(table, ["profile"], partiality_threshold=0.4) intensities.extend(table["intensity.prf.value"]) variances.extend(table["intensity.prf.variance"]) indices.extend(table["miller_index"]) batches.extend(table["batch"]) crystal_symmetry = miller.crystal.symmetry( unit_cell=determine_best_unit_cell(experiments), space_group=experiments[0].crystal.get_space_group(), assert_is_compatible_unit_cell=False, ) miller_set = miller.set(crystal_symmetry, indices, anomalous_flag=False) i_obs = miller.array(miller_set, data=intensities, sigmas=flex.sqrt(variances)) i_obs.set_observation_type_xray_intensity() i_obs.set_info(miller.array_info(source="DIALS", source_type="refl")) ms = i_obs.customized_copy() batch_array = miller.array(ms, data=batches) if params.reference is not None: reference, _ = miller_array_from_mtz(params.reference, anomalous=params.anomalous, labels=params.labels) else: reference = None return cls(i_obs, params, batches=batch_array, reference=reference)
def stats_only(reflections, experiments, params): """Calculate and print merging stats.""" best_unit_cell = params.reflection_selection.best_unit_cell if not params.reflection_selection.best_unit_cell: best_unit_cell = determine_best_unit_cell(experiments) scaled_miller_array = scaled_data_as_miller_array( reflections, experiments, best_unit_cell=best_unit_cell) try: res, _ = merging_stats_from_scaled_array( scaled_miller_array, params.output.merging.nbins, params.output.use_internal_variance, ) logger.info(make_merging_statistics_summary(res)) except DialsMergingStatisticsError as e: logger.info(e)
def export_mtz(integrated_data, experiment_list, params): """Export data from integrated_data corresponding to experiment_list to an MTZ file hklout.""" # if mtz filename is auto, then choose scaled.mtz or integrated.mtz if params.mtz.hklout in (None, Auto, "auto"): if ("intensity.scale.value" in integrated_data) and ("intensity.scale.variance" in integrated_data): params.mtz.hklout = "scaled.mtz" logger.info( "Data appears to be scaled, setting mtz.hklout = 'scaled.mtz'") else: params.mtz.hklout = "integrated.mtz" logger.info( "Data appears to be unscaled, setting mtz.hklout = 'integrated.mtz'" ) # First get the experiment identifier information out of the data expids_in_table = integrated_data.experiment_identifiers() if not list(expids_in_table.keys()): reflection_tables = parse_multiple_datasets([integrated_data]) experiment_list, refl_list = assign_unique_identifiers( experiment_list, reflection_tables) integrated_data = flex.reflection_table() for reflections in refl_list: integrated_data.extend(reflections) expids_in_table = integrated_data.experiment_identifiers() integrated_data.assert_experiment_identifiers_are_consistent( experiment_list) expids_in_list = list(experiment_list.identifiers()) # Convert experiment_list to a real python list or else identity assumptions # fail like: # assert experiment_list[0] is experiment_list[0] # And assumptions about added attributes break experiment_list = list(experiment_list) # Validate multi-experiment assumptions if len(experiment_list) > 1: # All experiments should match crystals, or else we need multiple crystals/datasets if not all(x.crystal == experiment_list[0].crystal for x in experiment_list[1:]): logger.warning( "Experiment crystals differ. Using first experiment crystal for file-level data." ) wavelengths = match_wavelengths(experiment_list) if len(wavelengths.keys()) > 1: logger.info( "Multiple wavelengths found: \n%s", "\n".join(" Wavlength: %.5f, experiment numbers: %s " % (k, ",".join(map(str, v))) for k, v in wavelengths.items()), ) else: wavelengths = OrderedDict( {experiment_list[0].beam.get_wavelength(): [0]}) # also only work correctly with one panel (for the moment) if any(len(experiment.detector) != 1 for experiment in experiment_list): logger.warning("Ignoring multiple panels in output MTZ") best_unit_cell = params.mtz.best_unit_cell if best_unit_cell is None: best_unit_cell = determine_best_unit_cell(experiment_list) integrated_data["d"] = best_unit_cell.d(integrated_data["miller_index"]) # Clean up the data with the passed in options integrated_data = filter_reflection_table( integrated_data, intensity_choice=params.intensity, partiality_threshold=params.mtz.partiality_threshold, combine_partials=params.mtz.combine_partials, min_isigi=params.mtz.min_isigi, filter_ice_rings=params.mtz.filter_ice_rings, d_min=params.mtz.d_min, ) # get batch offsets and image ranges - even for scanless experiments batch_offsets = [ expt.scan.get_batch_offset() for expt in experiment_list if expt.scan is not None ] unique_offsets = set(batch_offsets) if len(set(unique_offsets)) <= 1: logger.debug("Calculating new batches") batch_offsets = calculate_batch_offsets(experiment_list) batch_starts = [ e.scan.get_image_range()[0] if e.scan else 0 for e in experiment_list ] effective_offsets = [ o + s for o, s in zip(batch_offsets, batch_starts) ] unique_offsets = set(effective_offsets) else: logger.debug("Keeping existing batches") image_ranges = get_image_ranges(experiment_list) if len(unique_offsets) != len(batch_offsets): raise ValueError("Duplicate batch offsets detected: %s" % ", ".join( str(item) for item, count in Counter(batch_offsets).items() if count > 1)) # Create the mtz file mtz_writer = UnmergedMTZWriter( experiment_list[0].crystal.get_space_group()) # FIXME TODO for more than one experiment into an MTZ file: # # - add an epoch (or recover an epoch) from the scan and add this as an extra # column to the MTZ file for scaling, so we know that the two lattices were # integrated at the same time # ✓ decide a sensible BATCH increment to apply to the BATCH value between # experiments and add this for id_ in expids_in_table.keys(): # Grab our subset of the data loc = expids_in_list.index( expids_in_table[id_]) # get strid and use to find loc in list experiment = experiment_list[loc] if len(list(wavelengths.keys())) > 1: for i, (wl, exps) in enumerate(wavelengths.items()): if loc in exps: wavelength = wl dataset_id = i + 1 break else: wavelength = list(wavelengths.keys())[0] dataset_id = 1 reflections = integrated_data.select(integrated_data["id"] == id_) batch_offset = batch_offsets[loc] image_range = image_ranges[loc] reflections = assign_batches_to_reflections([reflections], [batch_offset])[0] experiment.data = dict(reflections) s0n = matrix.col(experiment.beam.get_s0()).normalize().elems logger.debug("Beam vector: %.4f %.4f %.4f" % s0n) mtz_writer.add_batch_list( image_range, experiment, wavelength, dataset_id, batch_offset=batch_offset, force_static_model=params.mtz.force_static_model, ) # Create the batch offset array. This gives us an experiment (id)-dependent # batch offset to calculate the correct batch from image number. experiment.data["batch_offset"] = flex.int(len(experiment.data["id"]), batch_offset) # Calculate whether we have a ROT value for this experiment, and set the column _, _, z = experiment.data["xyzcal.px"].parts() if experiment.scan: experiment.data[ "ROT"] = experiment.scan.get_angle_from_array_index(z) else: experiment.data["ROT"] = z mtz_writer.add_crystal( crystal_name=params.mtz.crystal_name, project_name=params.mtz.project_name, unit_cell=best_unit_cell, ) # Note: add unit cell here as may have changed basis since creating mtz. # For multi-wave unmerged mtz, we add an empty dataset for each wavelength, # but only write the data into the final dataset (for unmerged the batches # link the unmerged data to the individual wavelengths). for wavelength in wavelengths: mtz_writer.add_empty_dataset(wavelength) # Combine all of the experiment data columns before writing combined_data = { k: v.deep_copy() for k, v in experiment_list[0].data.items() } for experiment in experiment_list[1:]: for k, v in experiment.data.items(): combined_data[k].extend(v) # ALL columns must be the same length assert len({len(v) for v in combined_data.values() }) == 1, "Column length mismatch" assert len(combined_data["id"]) == len( integrated_data["id"]), "Lost rows in split/combine" # Write all the data and columns to the mtz file mtz_writer.write_columns(combined_data) logger.info("Saving {} integrated reflections to {}".format( len(combined_data["id"]), params.mtz.hklout)) mtz_file = mtz_writer.mtz_file mtz_file.write(params.mtz.hklout) return mtz_file
def merge_data_to_mtz(params, experiments, reflections): """Merge data (at each wavelength) and write to an mtz file object.""" wavelengths = match_wavelengths( experiments, absolute_tolerance=params.wavelength_tolerance, ) # wavelengths is an ordered dict mtz_datasets = [ MTZDataClass(wavelength=w, project_name=params.output.project_name) for w in wavelengths.keys() ] dataset_names = params.output.dataset_names crystal_names = params.output.crystal_names # check if best_unit_cell is set. best_unit_cell = params.best_unit_cell if not best_unit_cell: best_unit_cell = determine_best_unit_cell(experiments) reflections[0]["d"] = best_unit_cell.d(reflections[0]["miller_index"]) for expt in experiments: expt.crystal.unit_cell = best_unit_cell if len(wavelengths) > 1: logger.info( "Multiple wavelengths found: \n%s", "\n".join( " Wavlength: %.5f, experiment numbers: %s " % (k, ",".join(map(str, v))) for k, v in wavelengths.items() ), ) if not dataset_names or len(dataset_names) != len(wavelengths): logger.info( "Unequal number of dataset names and wavelengths, using default naming." ) dataset_names = [None] * len(wavelengths) if not crystal_names or len(crystal_names) != len(wavelengths): logger.info( "Unequal number of crystal names and wavelengths, using default naming." ) crystal_names = [None] * len(wavelengths) experiments_subsets = [] reflections_subsets = [] for dataset, dname, cname in zip(mtz_datasets, dataset_names, crystal_names): dataset.dataset_name = dname dataset.crystal_name = cname for exp_nos in wavelengths.values(): expids = [experiments[i].identifier for i in exp_nos] experiments_subsets.append( ExperimentList([experiments[i] for i in exp_nos]) ) reflections_subsets.append( reflections[0].select_on_experiment_identifiers(expids) ) else: mtz_datasets[0].dataset_name = dataset_names[0] mtz_datasets[0].crystal_name = crystal_names[0] experiments_subsets = [experiments] reflections_subsets = reflections # merge and truncate the data for each wavelength group for experimentlist, reflection_table, mtz_dataset in zip( experiments_subsets, reflections_subsets, mtz_datasets ): # First generate two merge_equivalents objects, collect merging stats merged, merged_anomalous, stats_summary = merge( experimentlist, reflection_table, d_min=params.d_min, d_max=params.d_max, combine_partials=params.combine_partials, partiality_threshold=params.partiality_threshold, best_unit_cell=best_unit_cell, anomalous=params.anomalous, assess_space_group=params.assess_space_group, n_bins=params.merging.n_bins, use_internal_variance=params.merging.use_internal_variance, ) merged_array = merged.array() # Save the relevant data in the mtz_dataset dataclass # This will add the data for IMEAN/SIGIMEAN mtz_dataset.merged_array = merged_array if merged_anomalous: merged_anomalous_array = merged_anomalous.array() # This will add the data for I(+), I(-), SIGI(+), SIGI(-), N(+), N(-) mtz_dataset.merged_anomalous_array = merged_anomalous_array mtz_dataset.multiplicities = merged_anomalous.redundancies() else: merged_anomalous_array = None # This will add the data for N mtz_dataset.multiplicities = merged.redundancies() if params.anomalous: merged_intensities = merged_anomalous_array else: merged_intensities = merged_array anom_amplitudes = None if params.truncate: amplitudes, anom_amplitudes, dano = truncate(merged_intensities) # This will add the data for F, SIGF mtz_dataset.amplitudes = amplitudes # This will add the data for F(+), F(-), SIGF(+), SIGF(-) mtz_dataset.anomalous_amplitudes = anom_amplitudes # This will add the data for DANO, SIGDANO mtz_dataset.dano = dano # print out analysis statistics show_wilson_scaling_analysis(merged_intensities) if stats_summary: logger.info(stats_summary) if anom_amplitudes: logger.info(make_dano_table(anom_amplitudes)) # pass the dataclasses to an MTZ writer to generate the mtz file and return. return make_merged_mtz_file(mtz_datasets)
def prepare_input(params, experiments, reflections): """Perform checks on the data and prepare the data for scaling. Raises: ValueError - a range of checks are made, a ValueError may be raised for a number of reasons. """ #### First exclude any datasets, before the dataset is split into #### individual reflection tables and expids set. if (params.dataset_selection.exclude_datasets or params.dataset_selection.use_datasets): experiments, reflections = select_datasets_on_ids( experiments, reflections, params.dataset_selection.exclude_datasets, params.dataset_selection.use_datasets, ) ids = flex.size_t() for r in reflections: ids.extend(r.experiment_identifiers().keys()) logger.info( "\nDataset ids for retained datasets are: %s \n", ",".join(str(i) for i in ids), ) #### Split the reflections tables into a list of reflection tables, #### with one table per experiment. logger.info("Checking for the existence of a reflection table \n" "containing multiple datasets \n") reflections = parse_multiple_datasets(reflections) logger.info( "Found %s reflection tables & %s experiments in total.", len(reflections), len(experiments), ) if len(experiments) != len(reflections): raise ValueError( "Mismatched number of experiments and reflection tables found.") #### Assign experiment identifiers. experiments, reflections = assign_unique_identifiers( experiments, reflections) ids = itertools.chain.from_iterable(r.experiment_identifiers().keys() for r in reflections) logger.info("\nDataset ids are: %s \n", ",".join(str(i) for i in ids)) for r in reflections: r.unset_flags(flex.bool(len(r), True), r.flags.bad_for_scaling) r.unset_flags(flex.bool(r.size(), True), r.flags.scaled) reflections, experiments = exclude_image_ranges_for_scaling( reflections, experiments, params.exclude_images) #### Allow checking of consistent indexing, useful for #### targeted / incremental scaling. if params.scaling_options.check_consistent_indexing: logger.info("Running dials.cosym to check consistent indexing:\n") cosym_params = cosym_phil_scope.extract() cosym_params.nproc = params.scaling_options.nproc cosym_instance = cosym(experiments, reflections, cosym_params) cosym_instance.run() experiments = cosym_instance.experiments reflections = cosym_instance.reflections logger.info("Finished running dials.cosym, continuing with scaling.\n") #### Make sure all experiments in same space group sgs = [ expt.crystal.get_space_group().type().number() for expt in experiments ] if len(set(sgs)) > 1: raise ValueError("""The experiments have different space groups: space group numbers found: %s Please reanalyse the data so that space groups are consistent, (consider using dials.reindex, dials.symmetry or dials.cosym) or remove incompatible experiments (using the option exclude_datasets=)""" % ", ".join(map(str, set(sgs)))) logger.info( "Space group being used during scaling is %s", experiments[0].crystal.get_space_group().info(), ) #### If doing targeted scaling, extract data and append an experiment #### and reflection table to the lists if params.scaling_options.target_model: logger.info("Extracting data from structural model.") exp, reflection_table = create_datastructures_for_structural_model( reflections, experiments, params.scaling_options.target_model) experiments.append(exp) reflections.append(reflection_table) elif params.scaling_options.target_mtz: logger.info("Extracting data from merged mtz.") exp, reflection_table = create_datastructures_for_target_mtz( experiments, params.scaling_options.target_mtz) experiments.append(exp) reflections.append(reflection_table) #### Perform any non-batch cutting of the datasets, including the target dataset best_unit_cell = params.reflection_selection.best_unit_cell if best_unit_cell is None: best_unit_cell = determine_best_unit_cell(experiments) for reflection in reflections: if params.cut_data.d_min or params.cut_data.d_max: d = best_unit_cell.d(reflection["miller_index"]) if params.cut_data.d_min: sel = d < params.cut_data.d_min reflection.set_flags(sel, reflection.flags.user_excluded_in_scaling) if params.cut_data.d_max: sel = d > params.cut_data.d_max reflection.set_flags(sel, reflection.flags.user_excluded_in_scaling) if params.cut_data.partiality_cutoff and "partiality" in reflection: reflection.set_flags( reflection["partiality"] < params.cut_data.partiality_cutoff, reflection.flags.user_excluded_in_scaling, ) return params, experiments, reflections
def merge_data_to_mtz(params, experiments, reflections): """Merge data (at each wavelength) and write to an mtz file object.""" wavelengths = match_wavelengths(experiments) # wavelengths is an ordered dict mtz_datasets = [ MTZDataClass(wavelength=w, project_name=params.output.project_name) for w in wavelengths.keys() ] dataset_names = params.output.dataset_names crystal_names = params.output.crystal_names # check if best_unit_cell is set. best_unit_cell = params.best_unit_cell if not best_unit_cell: best_unit_cell = determine_best_unit_cell(experiments) reflections[0]["d"] = best_unit_cell.d(reflections[0]["miller_index"]) for expt in experiments: expt.crystal.unit_cell = best_unit_cell if len(wavelengths) > 1: logger.info( "Multiple wavelengths found: \n%s", "\n".join( " Wavlength: %.5f, experiment numbers: %s " % (k, ",".join(map(str, v))) for k, v in wavelengths.items() ), ) if not dataset_names or len(dataset_names) != len(wavelengths): logger.info( "Unequal number of dataset names and wavelengths, using default naming." ) dataset_names = [None] * len(wavelengths) if not crystal_names or len(crystal_names) != len(wavelengths): logger.info( "Unequal number of crystal names and wavelengths, using default naming." ) crystal_names = [None] * len(wavelengths) experiments_subsets = [] reflections_subsets = [] for dataset, dname, cname in zip(mtz_datasets, dataset_names, crystal_names): dataset.dataset_name = dname dataset.crystal_name = cname for exp_nos in wavelengths.values(): expids = [experiments[i].identifier for i in exp_nos] experiments_subsets.append( ExperimentList([experiments[i] for i in exp_nos]) ) reflections_subsets.append( reflections[0].select_on_experiment_identifiers(expids) ) else: mtz_datasets[0].dataset_name = dataset_names[0] mtz_datasets[0].crystal_name = crystal_names[0] experiments_subsets = [experiments] reflections_subsets = reflections for experimentlist, reflection_table, mtz_dataset in zip( experiments_subsets, reflections_subsets, mtz_datasets ): # merge and truncate the data merged_array, merged_anomalous_array, stats_summary = merge( experimentlist, reflection_table, d_min=params.d_min, d_max=params.d_max, combine_partials=params.combine_partials, partiality_threshold=params.partiality_threshold, best_unit_cell=best_unit_cell, anomalous=params.anomalous, assess_space_group=params.assess_space_group, n_bins=params.merging.n_bins, use_internal_variance=params.merging.use_internal_variance, ) mtz_dataset.merged_array = merged_array mtz_dataset.merged_anomalous_array = merged_anomalous_array if params.anomalous: merged_intensities = merged_anomalous_array else: merged_intensities = merged_array if params.truncate: amplitudes, anomalous_amplitudes = truncate(merged_intensities) mtz_dataset.amplitudes = amplitudes mtz_dataset.anomalous_amplitudes = anomalous_amplitudes show_wilson_scaling_analysis(merged_intensities) if stats_summary: logger.info(stats_summary) return make_merged_mtz_file(mtz_datasets)
def export_shelx(scaled_data, experiment_list, params): """Export scaled data corresponding to experiment_list to a SHELX HKL formatted text file.""" # Handle requesting profile intensities (default via auto) but no column if "profile" in params.intensity and "intensity.prf.value" not in scaled_data: raise Sorry( "Requested profile intensity data but only summed present. Use intensity=sum." ) # use supplied best unit cell or that determined from experiment list to define d in reflection table. best_unit_cell = params.mtz.best_unit_cell if best_unit_cell is None: best_unit_cell = determine_best_unit_cell(experiment_list) else: logger.info("Using supplied unit cell across experiments : %s", best_unit_cell) scaled_data["d"] = best_unit_cell.d(scaled_data["miller_index"]) # Clean up reflection table with mtz defaults (as in export_xds_ascii) scaled_data = filter_reflection_table( scaled_data, intensity_choice=params.intensity, partiality_threshold=params.mtz.partiality_threshold, combine_partials=params.mtz.combine_partials, min_isigi=params.mtz.min_isigi, filter_ice_rings=params.mtz.filter_ice_rings, d_min=params.mtz.d_min, ) # Check that all experiments have the same space group if len({x.crystal.get_space_group().make_tidy() for x in experiment_list}) != 1: raise ValueError("Experiments do not have a unique space group") # Create miller set with space group from 1st crystal in experiment list and best unit cell miller_set = miller.set( crystal_symmetry=crystal.symmetry( unit_cell=best_unit_cell, space_group=experiment_list[0].crystal.get_space_group(), ), indices=scaled_data["miller_index"], anomalous_flag=False, ) intensity_choice = ( params.intensity[0] if params.intensity[0] != "profile" else "prf" ) intensities, variances = ( scaled_data["intensity." + intensity_choice + ".value"], scaled_data["intensity." + intensity_choice + ".variance"], ) assert variances.all_gt(0) i_obs = miller.array(miller_set, data=intensities) i_obs.set_observation_type_xray_intensity() i_obs.set_sigmas(flex.sqrt(variances)) # If requested scale up data to maximise use of scale_range if params.shelx.scale: max_val = max( i_obs.data().min_max_mean().max, i_obs.sigmas().min_max_mean().max ) min_val = min( i_obs.data().min_max_mean().min, i_obs.sigmas().min_max_mean().min ) min_scale = params.shelx.scale_range[0] / min_val max_scale = params.shelx.scale_range[1] / max_val scale = min(min_scale, max_scale) i_obs = i_obs.apply_scaling(factor=scale) # write the SHELX HKL file hkl_file = params.shelx.hklout with open(hkl_file, "w") as hkl_file_object: i_obs.export_as_shelx_hklf( file_object=hkl_file_object, scale_range=params.shelx.scale_range, normalise_if_format_overflow=True, ) logger.info(f"Written {i_obs.size()} relflections to {hkl_file}") # and a stub of an .ins file with information from the .expt file _write_ins( experiment_list, best_unit_cell=params.mtz.best_unit_cell, ins_file=params.shelx.ins, ) logger.info(f"Written {params.shelx.ins}")
def _write_ins(experiment_list, best_unit_cell, ins_file): sg = experiment_list[0].crystal.get_space_group() unit_cells = [] wavelengths = [] # Check for single wavelength for exp in experiment_list: wl = exp.beam.get_wavelength() if not any([isclose(wl, w, abs_tol=1e-4) for w in wavelengths]): wavelengths.append(wl) if len(wavelengths) > 1: raise ValueError("Experiments have more than one wavelength") else: wl = wavelengths[0] # if user has supplied best_unit_cell use it if best_unit_cell is not None: uc = best_unit_cell uc_sd = None else: for exp in experiment_list: unit_cells.append( exp.crystal.get_recalculated_unit_cell() or exp.crystal.get_unit_cell() ) if len(unit_cells) > 1: if ( len({uc.parameters() for uc in unit_cells}) > 1 ): # have different cells so no esds uc = determine_best_unit_cell(experiment_list) uc_sd = None else: # identical (recalculated?) unit cell with esds uc = ( experiment_list[0].crystal.get_recalculated_unit_cell() or experiment_list[0].crystal.get_unit_cell() ) uc_sd = ( experiment_list[0].crystal.get_recalculated_cell_parameter_sd() or experiment_list[0].crystal.get_cell_parameter_sd() ) else: # single unit cell uc = ( experiment_list[0].crystal.get_recalculated_unit_cell() or experiment_list[0].crystal.get_unit_cell() ) uc_sd = ( experiment_list[0].crystal.get_recalculated_cell_parameter_sd() or experiment_list[0].crystal.get_cell_parameter_sd() ) with open(ins_file, "w") as f: f.write( f"TITL {sg.type().number()} in {sg.type().lookup_symbol().replace(' ','')}\n" ) f.write( "CELL {:8.5f} {:8.4f} {:8.4f} {:8.4f} {:8.3f} {:8.3f} {:8.3f}\n".format( wl, *uc.parameters() ) ) if uc_sd: f.write( "ZERR {:8.3f} {:8.4f} {:8.4f} {:8.4f} {:8.3f} {:8.3f} {:8.3f}\n".format( sg.order_z(), *uc_sd ) ) LATT_SYMM(f, sg)
def run(args=None): import libtbx.load_env from dials.util import Sorry usage = "dials.reindex [options] indexed.expt indexed.refl" parser = ArgumentParser( usage=usage, phil=phil_scope, read_reflections=True, read_experiments=True, check_format=False, epilog=help_message, ) params, options = parser.parse_args(args, show_diff_phil=True) reflections, experiments = reflections_and_experiments_from_files( params.input.reflections, params.input.experiments ) if len(experiments) == 0 and len(reflections) == 0: parser.print_help() return if params.change_of_basis_op is None: raise Sorry("Please provide a change_of_basis_op.") reference_crystal = None if params.reference.experiments is not None: from dxtbx.serialize import load reference_experiments = load.experiment_list( params.reference.experiments, check_format=False ) if len(reference_experiments.crystals()) == 1: reference_crystal = reference_experiments.crystals()[0] else: # first check sg all same sgs = [ expt.crystal.get_space_group().type().number() for expt in experiments ] if len(set(sgs)) > 1: raise Sorry( """The reference experiments have different space groups: space group numbers found: %s Please reanalyse the data so that space groups are consistent, (consider using dials.reindex, dials.symmetry or dials.cosym)""" % ", ".join(map(str, set(sgs))) ) reference_crystal = reference_experiments.crystals()[0] reference_crystal.unit_cell = determine_best_unit_cell( reference_experiments ) if params.reference.reflections is not None: # First check that we have everything as expected for the reference reindexing if params.reference.experiments is None: raise Sorry( """For reindexing against a reference dataset, a reference experiments file must also be specified with the option: reference.experiments= """ ) if not os.path.exists(params.reference.reflections): raise Sorry("Could not locate reference dataset reflection file") reference_reflections = flex.reflection_table().from_file( params.reference.reflections ) test_reflections = reflections[0] if ( reference_crystal.get_space_group().type().number() != experiments.crystals()[0].get_space_group().type().number() ): raise Sorry("Space group of input does not match reference") # Set some flags to allow filtering, if wanting to reindex against # reference with data that has not yet been through integration if ( test_reflections.get_flags(test_reflections.flags.integrated_sum).count( True ) == 0 ): assert ( "intensity.sum.value" in test_reflections ), "No 'intensity.sum.value' in reflections" test_reflections.set_flags( flex.bool(test_reflections.size(), True), test_reflections.flags.integrated_sum, ) if ( reference_reflections.get_flags( reference_reflections.flags.integrated_sum ).count(True) == 0 ): assert ( "intensity.sum.value" in test_reflections ), "No 'intensity.sum.value in reference reflections" reference_reflections.set_flags( flex.bool(reference_reflections.size(), True), reference_reflections.flags.integrated_sum, ) # Make miller array of the two datasets try: test_miller_set = filtered_arrays_from_experiments_reflections( experiments, [test_reflections] )[0] except ValueError: raise Sorry("No reflections remain after filtering the test dataset") try: reference_miller_set = filtered_arrays_from_experiments_reflections( reference_experiments, [reference_reflections] )[0] except ValueError: raise Sorry("No reflections remain after filtering the reference dataset") from dials.algorithms.symmetry.reindex_to_reference import ( determine_reindex_operator_against_reference, ) change_of_basis_op = determine_reindex_operator_against_reference( test_miller_set, reference_miller_set ) elif len(experiments) and params.change_of_basis_op is libtbx.Auto: if reference_crystal is not None: if len(experiments.crystals()) > 1: raise Sorry("Only one crystal can be processed at a time") from dials.algorithms.indexing.compare_orientation_matrices import ( difference_rotation_matrix_axis_angle, ) cryst = experiments.crystals()[0] R, axis, angle, change_of_basis_op = difference_rotation_matrix_axis_angle( cryst, reference_crystal ) print(f"Change of basis op: {change_of_basis_op}") print("Rotation matrix to transform input crystal to reference::") print(R.mathematica_form(format="%.3f", one_row_per_line=True)) print( f"Rotation of {angle:.3f} degrees", "about axis (%.3f, %.3f, %.3f)" % axis, ) elif len(reflections): assert len(reflections) == 1 # always re-map reflections to reciprocal space refl = reflections.deep_copy() refl.centroid_px_to_mm(experiments) refl.map_centroids_to_reciprocal_space(experiments) # index the reflection list using the input experiments list refl["id"] = flex.int(len(refl), -1) index = AssignIndicesGlobal(tolerance=0.2) index(refl, experiments) hkl_expt = refl["miller_index"] hkl_input = reflections[0]["miller_index"] change_of_basis_op = derive_change_of_basis_op(hkl_input, hkl_expt) # reset experiments list since we don't want to reindex this experiments = [] else: change_of_basis_op = sgtbx.change_of_basis_op(params.change_of_basis_op) if len(experiments): space_group = params.space_group if space_group is not None: space_group = space_group.group() try: experiments = reindex_experiments( experiments, change_of_basis_op, space_group=space_group ) except RuntimeError as e: # Only catch specific errors here if "Unsuitable value for rational rotation matrix." in str(e): original_message = str(e).split(":")[-1].strip() sys.exit(f"Error: {original_message} Is your change_of_basis_op valid?") raise print(f"Saving reindexed experimental models to {params.output.experiments}") experiments.as_file(params.output.experiments) if len(reflections): assert len(reflections) == 1 reflections = reflections[0] miller_indices = reflections["miller_index"] if params.hkl_offset is not None: h, k, l = miller_indices.as_vec3_double().parts() h += params.hkl_offset[0] k += params.hkl_offset[1] l += params.hkl_offset[2] miller_indices = flex.miller_index(h.iround(), k.iround(), l.iround()) non_integral_indices = change_of_basis_op.apply_results_in_non_integral_indices( miller_indices ) if non_integral_indices.size() > 0: print( "Removing %i/%i reflections (change of basis results in non-integral indices)" % (non_integral_indices.size(), miller_indices.size()) ) sel = flex.bool(miller_indices.size(), True) sel.set_selected(non_integral_indices, False) miller_indices_reindexed = change_of_basis_op.apply(miller_indices.select(sel)) reflections["miller_index"].set_selected(sel, miller_indices_reindexed) reflections["miller_index"].set_selected(~sel, (0, 0, 0)) print(f"Saving reindexed reflections to {params.output.reflections}") reflections.as_file(params.output.reflections)