def refine(self, experiments, reflections): acceptance_flags = self.identify_outliers(self.all_params, experiments, reflections) # create a new "reflections" list with outliers thrown out: reflections = reflections.select(acceptance_flags) R = e_refine( params=self.all_params, experiments=experiments, reflections=reflections, graph_verbose=False, ) ref_experiments = R.get_experiments() # try to improve the outcome with a second round of outlier rejection post-initial refinement: acceptance_flags = self.identify_outliers(self.all_params, ref_experiments, reflections) # insert a round of Nave-outlier rejection on top of the r.m.s.d. rejection nv0 = NaveParameters( params=self.all_params, experiments=ref_experiments, reflections=reflections, refinery=R, graph_verbose=False, ) nv0() acceptance_flags_nv0 = nv0.nv_acceptance_flags reflections = reflections.select(acceptance_flags & acceptance_flags_nv0) R = e_refine( params=self.all_params, experiments=ref_experiments, reflections=reflections, graph_verbose=False, ) ref_experiments = R.get_experiments() nv = NaveParameters( params=self.all_params, experiments=ref_experiments, reflections=reflections, refinery=R, graph_verbose=False, ) nv() rmsd, _ = calc_2D_rmsd_and_displacements( R.predict_for_reflection_table(reflections)) matches = R.get_matches() xyzcal_mm = flex.vec3_double(len(reflections)) xyzcal_mm.set_selected(matches["iobs"], matches["xyzcal.mm"]) reflections["xyzcal.mm"] = xyzcal_mm reflections.set_flags(matches["iobs"], reflections.flags.used_in_refinement) reflections["entering"] = flex.bool(len(reflections), False) return ref_experiments, reflections
def run(self): ''' Parse the options. ''' from dials.util.options import flatten_experiments, flatten_reflections # Parse the command line arguments params, options = self.parser.parse_args(show_diff_phil=True) experiments = flatten_experiments(params.input.experiments) reflections = flatten_reflections(params.input.reflections) assert len(reflections) == 1 reflections = reflections[0] domain_size = flex.double() mosaic_angle = flex.double() filtered_reflections = flex.reflection_table() for i in range(len(experiments)): refls = reflections.select(reflections['id'] == i) try: nv = NaveParameters(params=None, experiments=experiments[i:i + 1], reflections=refls, refinery=None, graph_verbose=False) crystal_model_nv = nv() except Exception as e: continue domain_size.append(experiments[i].crystal.get_domain_size_ang() - crystal_model_nv.get_domain_size_ang()) mosaic_angle.append( experiments[i].crystal.get_half_mosaicity_deg() - crystal_model_nv.get_half_mosaicity_deg()) experiments[i].crystal = crystal_model_nv refls = refls.select(nv.nv_acceptance_flags) filtered_reflections.extend(refls) print("Saving new experiments as %s" % params.output.experiments) dump = ExperimentListDumper(experiments) dump.as_json(params.output.experiments) print("Removed %d out of %d reflections as outliers" % (len(reflections) - len(filtered_reflections), len(reflections))) print("Saving filtered reflections as %s" % params.output.experiments) filtered_reflections.as_pickle(params.output.reflections) if params.plot_changes: from matplotlib import pyplot as plt domain_size = domain_size.select((domain_size >= -10) & (domain_size <= 10)) mosaic_angle = mosaic_angle.select((mosaic_angle >= -0.1) & (mosaic_angle <= 0.1)) for d in [domain_size, mosaic_angle]: f = plt.figure() plt.hist(d, bins=30) plt.show()
def refine(self, experiments, centroids): if self.params.dispatch.refine: from dials.algorithms.refinement import RefinerFactory refiner = RefinerFactory.from_parameters_data_experiments( self.params, centroids, experiments ) refiner.run() experiments = refiner.get_experiments() predicted = refiner.predict_for_indexed() centroids["xyzcal.mm"] = predicted["xyzcal.mm"] centroids["entering"] = predicted["entering"] centroids = centroids.select(refiner.selection_used_for_refinement()) # Re-estimate mosaic estimates from dials.algorithms.indexing.nave_parameters import NaveParameters nv = NaveParameters( params=self.params, experiments=experiments, reflections=centroids, refinery=refiner, graph_verbose=False, ) nv() acceptance_flags_nv = nv.nv_acceptance_flags centroids = centroids.select(acceptance_flags_nv) # Dump experiments to disk if self.params.output.refined_experiments_filename: experiments.as_json(self.params.output.refined_experiments_filename) if self.params.output.indexed_filename: self.save_reflections(centroids, self.params.output.indexed_filename) return experiments, centroids
def index(self): # most of this is the same as dials.algorithms.indexing.indexer.indexer_base.index(), with some stills # specific modifications (don't re-index after choose best orientation matrix, but use the indexing from # choose best orientation matrix, also don't use macrocycles) of refinement after indexing. # 2017 update: do accept multiple lattices per shot experiments = ExperimentList() while True: self.d_min = self.params.refinement_protocol.d_min_start max_lattices = self.params.multiple_lattice_search.max_lattices if max_lattices is not None and len(experiments) >= max_lattices: break if len(experiments) > 0: cutoff_fraction = ( self.params.multiple_lattice_search.recycle_unindexed_reflections_cutoff ) d_spacings = 1 / self.reflections["rlp"].norms() d_min_indexed = flex.min(d_spacings.select(self.indexed_reflections)) min_reflections_for_indexing = cutoff_fraction * len( self.reflections.select(d_spacings > d_min_indexed) ) crystal_ids = self.reflections.select(d_spacings > d_min_indexed)["id"] if (crystal_ids == -1).count(True) < min_reflections_for_indexing: logger.info( "Finish searching for more lattices: %i unindexed reflections remaining." % (min_reflections_for_indexing) ) break n_lattices_previous_cycle = len(experiments) # index multiple lattices per shot if len(experiments) == 0: new = self.find_lattices() generate_experiment_identifiers(new) experiments.extend(new) if len(experiments) == 0: raise DialsIndexError("No suitable lattice could be found.") else: try: new = self.find_lattices() generate_experiment_identifiers(new) experiments.extend(new) except Exception as e: logger.info("Indexing remaining reflections failed") logger.debug( "Indexing remaining reflections failed, exception:\n" + str(e) ) # reset reflection lattice flags # the lattice a given reflection belongs to: a value of -1 indicates # that a reflection doesn't belong to any lattice so far self.reflections["id"] = flex.int(len(self.reflections), -1) self.index_reflections(experiments, self.reflections) if len(experiments) == n_lattices_previous_cycle: # no more lattices found break if ( not self.params.stills.refine_candidates_with_known_symmetry and self.params.known_symmetry.space_group is not None ): self._apply_symmetry_post_indexing( experiments, self.reflections, n_lattices_previous_cycle ) # discard nearly overlapping lattices on the same shot if self._check_have_similar_crystal_models(experiments): break self.indexed_reflections = self.reflections["id"] > -1 if self.d_min is None: sel = self.reflections["id"] <= -1 else: sel = flex.bool(len(self.reflections), False) lengths = 1 / self.reflections["rlp"].norms() isel = (lengths >= self.d_min).iselection() sel.set_selected(isel, True) sel.set_selected(self.reflections["id"] > -1, False) self.unindexed_reflections = self.reflections.select(sel) reflections_for_refinement = self.reflections.select( self.indexed_reflections ) if len(self.params.stills.isoforms) > 0: logger.info("") logger.info("#" * 80) logger.info("Starting refinement") logger.info("#" * 80) logger.info("") isoform_experiments = ExperimentList() isoform_reflections = flex.reflection_table() # Note, changes to params after initial indexing. Cannot use tie to target when fixing the unit cell. self.all_params.refinement.reflections.outlier.algorithm = "null" self.all_params.refinement.parameterisation.crystal.fix = "cell" self.all_params.refinement.parameterisation.crystal.unit_cell.restraints.tie_to_target = ( [] ) for expt_id, experiment in enumerate(experiments): reflections = reflections_for_refinement.select( reflections_for_refinement["id"] == expt_id ) reflections["id"] = flex.int(len(reflections), 0) refiners = [] for isoform in self.params.stills.isoforms: iso_experiment = copy.deepcopy(experiment) crystal = iso_experiment.crystal if ( isoform.lookup_symbol != crystal.get_space_group().type().lookup_symbol() ): logger.info( "Crystal isoform lookup_symbol %s does not match isoform %s lookup_symbol %s" % ( crystal.get_space_group().type().lookup_symbol(), isoform.name, isoform.lookup_symbol, ) ) continue crystal.set_B(isoform.cell.fractionalization_matrix()) logger.info("Refining isoform %s" % isoform.name) refiners.append( e_refine( params=self.all_params, experiments=ExperimentList([iso_experiment]), reflections=reflections, graph_verbose=False, ) ) if len(refiners) == 0: raise DialsIndexError( "No isoforms had a lookup symbol that matched" ) positional_rmsds = [ math.sqrt(P.rmsds()[0] ** 2 + P.rmsds()[1] ** 2) for P in refiners ] logger.info( "Positional rmsds for all isoforms:" + str(positional_rmsds) ) minrmsd_mm = min(positional_rmsds) minindex = positional_rmsds.index(minrmsd_mm) logger.info( "The smallest rmsd is %5.1f um from isoform %s" % ( 1000.0 * minrmsd_mm, self.params.stills.isoforms[minindex].name, ) ) if self.params.stills.isoforms[minindex].rmsd_target_mm is not None: logger.info( "Asserting %f < %f" % ( minrmsd_mm, self.params.stills.isoforms[minindex].rmsd_target_mm, ) ) assert ( minrmsd_mm < self.params.stills.isoforms[minindex].rmsd_target_mm ) logger.info( "Acceptable rmsd for isoform %s." % (self.params.stills.isoforms[minindex].name) ) if len(self.params.stills.isoforms) == 2: logger.info( "Rmsd gain over the other isoform %5.1f um." % (1000.0 * abs(positional_rmsds[0] - positional_rmsds[1])) ) R = refiners[minindex] # Now one last check to see if direct beam is out of bounds if self.params.stills.isoforms[minindex].beam_restraint is not None: from scitbx import matrix refined_beam = matrix.col( R.get_experiments()[0] .detector[0] .get_beam_centre_lab(experiments[0].beam.get_s0())[0:2] ) known_beam = matrix.col( self.params.stills.isoforms[minindex].beam_restraint ) logger.info( "Asserting difference in refined beam center and expected beam center %f < %f" % ( (refined_beam - known_beam).length(), self.params.stills.isoforms[minindex].rmsd_target_mm, ) ) assert ( (refined_beam - known_beam).length() < self.params.stills.isoforms[minindex].rmsd_target_mm ) # future--circle of confusion could be given as a separate length in mm instead of reusing rmsd_target experiment = R.get_experiments()[0] experiment.crystal.identified_isoform = self.params.stills.isoforms[ minindex ].name isoform_experiments.append(experiment) reflections["id"] = flex.int(len(reflections), expt_id) isoform_reflections.extend(reflections) experiments = isoform_experiments reflections_for_refinement = isoform_reflections if self.params.refinement_protocol.mode == "repredict_only": from dials.algorithms.indexing.nave_parameters import NaveParameters from dials.algorithms.refinement.prediction.managed_predictors import ( ExperimentsPredictorFactory, ) refined_experiments, refined_reflections = ( experiments, reflections_for_refinement, ) ref_predictor = ExperimentsPredictorFactory.from_experiments( experiments, force_stills=True, spherical_relp=self.all_params.refinement.parameterisation.spherical_relp_model, ) ref_predictor(refined_reflections) refined_reflections["delpsical2"] = ( refined_reflections["delpsical.rad"] ** 2 ) for expt_id in range(len(refined_experiments)): refls = refined_reflections.select( refined_reflections["id"] == expt_id ) nv = NaveParameters( params=self.all_params, experiments=refined_experiments[expt_id : expt_id + 1], reflections=refls, refinery=None, graph_verbose=False, ) experiments[expt_id].crystal = nv() ref_predictor = ExperimentsPredictorFactory.from_experiments( experiments, force_stills=True, spherical_relp=self.all_params.refinement.parameterisation.spherical_relp_model, ) ref_predictor(refined_reflections) elif self.params.refinement_protocol.mode is None: refined_experiments, refined_reflections = ( experiments, reflections_for_refinement, ) else: try: refined_experiments, refined_reflections = self.refine( experiments, reflections_for_refinement ) except Exception as e: s = str(e) if len(experiments) == 1: raise DialsIndexRefineError(e.message) logger.info("Refinement failed:") logger.info(s) del experiments[-1] break self._unit_cell_volume_sanity_check(experiments, refined_experiments) self.refined_reflections = refined_reflections.select( refined_reflections["id"] > -1 ) for i, expt in enumerate(self.experiments): ref_sel = self.refined_reflections.select( self.refined_reflections["imageset_id"] == i ) ref_sel = ref_sel.select(ref_sel["id"] >= 0) for i_expt in set(ref_sel["id"]): refined_expt = refined_experiments[i_expt] expt.detector = refined_expt.detector expt.beam = refined_expt.beam expt.goniometer = refined_expt.goniometer expt.scan = refined_expt.scan refined_expt.imageset = expt.imageset if not ( self.all_params.refinement.parameterisation.beam.fix == "all" and self.all_params.refinement.parameterisation.detector.fix == "all" ): # Experimental geometry may have changed - re-map centroids to # reciprocal space self.reflections.map_centroids_to_reciprocal_space(self.experiments) # update for next cycle experiments = refined_experiments self.refined_experiments = refined_experiments if self.refined_experiments is None: raise DialsIndexRefineError("None of the experiments could refine.") # discard experiments with zero reflections after refinement id_set = set(self.refined_reflections["id"]) if len(id_set) < len(self.refined_experiments): filtered_refined_reflections = flex.reflection_table() for i in range(len(self.refined_experiments)): if i not in id_set: del self.refined_experiments[i] for old, new in zip(sorted(id_set), range(len(id_set))): subset = self.refined_reflections.select( self.refined_reflections["id"] == old ) subset["id"] = flex.int(len(subset), new) filtered_refined_reflections.extend(subset) self.refined_reflections = filtered_refined_reflections if len(self.refined_experiments) > 1: from dials.algorithms.indexing.compare_orientation_matrices import ( rotation_matrix_differences, ) logger.info( rotation_matrix_differences(self.refined_experiments.crystals()) ) logger.info("Final refined crystal models:") for i, crystal_model in enumerate(self.refined_experiments.crystals()): n_indexed = 0 for _ in experiments.where(crystal=crystal_model): n_indexed += (self.reflections["id"] == i).count(True) logger.info("model %i (%i reflections):" % (i + 1, n_indexed)) logger.info(crystal_model) if ( "xyzcal.mm" in self.refined_reflections ): # won't be there if refine_all_candidates = False and no isoforms self._xyzcal_mm_to_px(self.experiments, self.refined_reflections)
def choose_best_orientation_matrix(self, candidate_orientation_matrices): logger.info("*" * 80) logger.info("Selecting the best orientation matrix") logger.info("*" * 80) class CandidateInfo(libtbx.group_args): pass candidates = [] params = copy.deepcopy(self.all_params) for icm, cm in enumerate(candidate_orientation_matrices): if icm >= self.params.basis_vector_combinations.max_refine: break # Index reflections in P1 sel = self.reflections["id"] == -1 refl = self.reflections.select(sel) experiments = self.experiment_list_for_crystal(cm) self.index_reflections(experiments, refl) indexed = refl.select(refl["id"] >= 0) indexed = indexed.select(indexed.get_flags(indexed.flags.indexed)) # If target symmetry supplied, try to apply it. Then, apply the change of basis to the reflections # indexed in P1 to the target setting if ( self.params.stills.refine_candidates_with_known_symmetry and self.params.known_symmetry.space_group is not None ): new_crystal, cb_op_to_primitive = self._symmetry_handler.apply_symmetry( cm ) if new_crystal is None: logger.info("Cannot convert to target symmetry, candidate %d", icm) continue new_crystal = new_crystal.change_basis( self._symmetry_handler.cb_op_primitive_inp ) cm = new_crystal experiments = self.experiment_list_for_crystal(cm) if not cb_op_to_primitive.is_identity_op(): indexed["miller_index"] = cb_op_to_primitive.apply( indexed["miller_index"] ) if self._symmetry_handler.cb_op_primitive_inp is not None: indexed[ "miller_index" ] = self._symmetry_handler.cb_op_primitive_inp.apply( indexed["miller_index"] ) if params.indexing.stills.refine_all_candidates: try: logger.info( "$$$ stills_indexer::choose_best_orientation_matrix, candidate %d initial outlier identification", icm, ) acceptance_flags = self.identify_outliers( params, experiments, indexed ) # create a new "indexed" list with outliers thrown out: indexed = indexed.select(acceptance_flags) logger.info( "$$$ stills_indexer::choose_best_orientation_matrix, candidate %d refinement before outlier rejection", icm, ) R = e_refine( params=params, experiments=experiments, reflections=indexed, graph_verbose=False, ) ref_experiments = R.get_experiments() # try to improve the outcome with a second round of outlier rejection post-initial refinement: acceptance_flags = self.identify_outliers( params, ref_experiments, indexed ) # insert a round of Nave-outlier rejection on top of the r.m.s.d. rejection nv0 = NaveParameters( params=params, experiments=ref_experiments, reflections=indexed, refinery=R, graph_verbose=False, ) nv0() acceptance_flags_nv0 = nv0.nv_acceptance_flags indexed = indexed.select(acceptance_flags & acceptance_flags_nv0) logger.info( "$$$ stills_indexer::choose_best_orientation_matrix, candidate %d after positional and delta-psi outlier rejection", icm, ) R = e_refine( params=params, experiments=ref_experiments, reflections=indexed, graph_verbose=False, ) ref_experiments = R.get_experiments() nv = NaveParameters( params=params, experiments=ref_experiments, reflections=indexed, refinery=R, graph_verbose=False, ) crystal_model = nv() assert ( len(crystal_model) == 1 ), "$$$ stills_indexer::choose_best_orientation_matrix, Only one crystal at this stage" crystal_model = crystal_model[0] # Drop candidates that after refinement can no longer be converted to the known target space group if ( not self.params.stills.refine_candidates_with_known_symmetry and self.params.known_symmetry.space_group is not None ): ( new_crystal, cb_op_to_primitive, ) = self._symmetry_handler.apply_symmetry(crystal_model) if new_crystal is None: logger.info( "P1 refinement yielded model diverged from target, candidate %d", icm, ) continue rmsd, _ = calc_2D_rmsd_and_displacements( R.predict_for_reflection_table(indexed) ) except Exception as e: logger.info( "Couldn't refine candidate %d, %s: %s", icm, e.__class__.__name__, str(e), ) else: logger.info( "$$$ stills_indexer::choose_best_orientation_matrix, candidate %d done", icm, ) candidates.append( CandidateInfo( crystal=crystal_model, green_curve_area=nv.green_curve_area, ewald_proximal_volume=nv.ewald_proximal_volume(), n_indexed=len(indexed), rmsd=rmsd, indexed=indexed, experiments=ref_experiments, ) ) else: from dials.algorithms.refinement.prediction.managed_predictors import ( ExperimentsPredictorFactory, ) ref_predictor = ExperimentsPredictorFactory.from_experiments( experiments, force_stills=True, spherical_relp=params.refinement.parameterisation.spherical_relp_model, ) rmsd, _ = calc_2D_rmsd_and_displacements(ref_predictor(indexed)) candidates.append( CandidateInfo( crystal=cm, n_indexed=len(indexed), rmsd=rmsd, indexed=indexed, experiments=experiments, ) ) if len(candidates) == 0: raise DialsIndexError("No suitable indexing solution found") logger.info("**** ALL CANDIDATES:") for i, XX in enumerate(candidates): logger.info("\n****Candidate %d %s", i, XX) cc = XX.crystal if hasattr(cc, "get_half_mosaicity_deg"): logger.info( " half mosaicity %5.2f deg.", (cc.get_half_mosaicity_deg()) ) logger.info(" domain size %.0f Ang.", (cc.get_domain_size_ang())) logger.info("\n**** BEST CANDIDATE:") results = flex.double([c.rmsd for c in candidates]) best = candidates[flex.min_index(results)] logger.info(best) if params.indexing.stills.refine_all_candidates: if best.rmsd > params.indexing.stills.rmsd_min_px: raise DialsIndexError("RMSD too high, %f" % best.rmsd) if len(candidates) > 1: for i in range(len(candidates)): if i == flex.min_index(results): continue if best.ewald_proximal_volume > candidates[i].ewald_proximal_volume: logger.info( "Couldn't figure out which candidate is best; picked the one with the best RMSD." ) best.indexed["entering"] = flex.bool(best.n_indexed, False) return best.crystal, best.n_indexed