def real_space_grid_search(self): d_min = self.params.refinement_protocol.d_min_start sel = (self.reflections['id'] == -1) if d_min is not None: sel &= (1/self.reflections['rlp'].norms() > d_min) reciprocal_lattice_points = self.reflections['rlp'].select(sel) logger.info("Indexing from %i reflections" %len(reciprocal_lattice_points)) def compute_functional(vector): two_pi_S_dot_v = 2 * math.pi * reciprocal_lattice_points.dot(vector) return flex.sum(flex.cos(two_pi_S_dot_v)) from rstbx.array_family import flex from rstbx.dps_core import SimpleSamplerTool assert self.target_symmetry_primitive is not None assert self.target_symmetry_primitive.unit_cell() is not None SST = SimpleSamplerTool( self.params.real_space_grid_search.characteristic_grid) SST.construct_hemisphere_grid(SST.incr) cell_dimensions = self.target_symmetry_primitive.unit_cell().parameters()[:3] unique_cell_dimensions = set(cell_dimensions) logger.info( "Number of search vectors: %i" %(len(SST.angles) * len(unique_cell_dimensions))) vectors = flex.vec3_double() function_values = flex.double() for i, direction in enumerate(SST.angles): for l in unique_cell_dimensions: v = matrix.col(direction.dvec) * l f = compute_functional(v.elems) vectors.append(v.elems) function_values.append(f) perm = flex.sort_permutation(function_values, reverse=True) vectors = vectors.select(perm) function_values = function_values.select(perm) unique_vectors = [] i = 0 while len(unique_vectors) < 30: v = matrix.col(vectors[i]) is_unique = True if i > 0: for v_u in unique_vectors: if v.length() < v_u.length(): if is_approximate_integer_multiple(v, v_u): is_unique = False break elif is_approximate_integer_multiple(v_u, v): is_unique = False break if is_unique: unique_vectors.append(v) i += 1 for i in range(30): v = matrix.col(vectors[i]) logger.debug("%s %s %s" %(str(v.elems), str(v.length()), str(function_values[i]))) basis_vectors = [v.elems for v in unique_vectors] self.candidate_basis_vectors = basis_vectors if self.params.optimise_initial_basis_vectors: optimised_basis_vectors = optimise_basis_vectors( reciprocal_lattice_points, basis_vectors) optimised_function_values = flex.double([ compute_functional(v) for v in optimised_basis_vectors]) perm = flex.sort_permutation(optimised_function_values, reverse=True) optimised_basis_vectors = optimised_basis_vectors.select(perm) optimised_function_values = optimised_function_values.select(perm) unique_vectors = [matrix.col(v) for v in optimised_basis_vectors] logger.info("Number of unique vectors: %i" %len(unique_vectors)) for i in range(len(unique_vectors)): logger.debug("%s %s %s" %( str(compute_functional(unique_vectors[i].elems)), str(unique_vectors[i].length()), str(unique_vectors[i].elems))) crystal_models = [] self.candidate_basis_vectors = unique_vectors self.debug_show_candidate_basis_vectors() if self.params.debug_plots: self.debug_plot_candidate_basis_vectors() candidate_orientation_matrices \ = self.find_candidate_orientation_matrices( unique_vectors, max_combinations=self.params.basis_vector_combinations.max_try) crystal_model, n_indexed = self.choose_best_orientation_matrix( candidate_orientation_matrices) if crystal_model is not None: crystal_models = [crystal_model] else: crystal_models = [] #assert len(crystal_models) > 0 candidate_orientation_matrices = crystal_models #for i in range(len(candidate_orientation_matrices)): #if self.target_symmetry_primitive is not None: ##print "symmetrizing model" ##self.target_symmetry_primitive.show_summary() #symmetrized_model = self.apply_symmetry( #candidate_orientation_matrices[i], self.target_symmetry_primitive) #candidate_orientation_matrices[i] = symmetrized_model self.candidate_crystal_models = candidate_orientation_matrices
def two_color_grid_search(self): '''creates candidate reciprocal lattice points based on two beams and performs 2-D grid search based on maximizing the functional using N_UNIQUE_V candidate vectors (N_UNIQUE_V is usually 30 from Guildea paper)''' assert len(self.imagesets) == 1 detector = self.imagesets[0].get_detector() mm_spot_pos = self.map_spots_pixel_to_mm_rad(self.reflections,detector,scan=None) self.map_centroids_to_reciprocal_space(mm_spot_pos,detector,self.beams[0], goniometer=None) self.reciprocal_lattice_points1 = mm_spot_pos['rlp'].select( (self.reflections['id'] == -1)) rlps1 = mm_spot_pos['rlp'].select( (self.reflections['id'] == -1)) self.map_centroids_to_reciprocal_space(mm_spot_pos,detector,self.beams[1], goniometer=None) self.reciprocal_lattice_points2 = mm_spot_pos['rlp'].select( (self.reflections['id'] == -1)) # assert len(self.beams) == 3 rlps2 = mm_spot_pos['rlp'].select( (self.reflections['id'] == -1)) self.reciprocal_lattice_points=rlps1.concatenate(rlps2) #self.map_centroids_to_reciprocal_space(mm_spot_pos,detector,self.beams[2],goniometer=None) #self.reciprocal_lattice_points = mm_spot_pos['rlp'].select( # (self.reflections['id'] == -1)&(1/self.reflections['rlp'].norms() > d_min)) print "Indexing from %i reflections" %len(self.reciprocal_lattice_points) def compute_functional(vector): '''computes functional for 2-D grid search''' two_pi_S_dot_v = 2 * math.pi * self.reciprocal_lattice_points.dot(vector) return flex.sum(flex.cos(two_pi_S_dot_v)) from rstbx.array_family import flex from rstbx.dps_core import SimpleSamplerTool assert self.target_symmetry_primitive is not None assert self.target_symmetry_primitive.unit_cell() is not None SST = SimpleSamplerTool( self.params.real_space_grid_search.characteristic_grid) SST.construct_hemisphere_grid(SST.incr) cell_dimensions = self.target_symmetry_primitive.unit_cell().parameters()[:3] unique_cell_dimensions = set(cell_dimensions) print("Makring search vecs") spiral_method = True if spiral_method: basis_vec_noise =True noise_scale = 2. #_N = 200000 # massively oversample the hemisphere so we can apply noise to our search _N = 100000 print "Number of search vectors: %i" %( _N * len(unique_cell_dimensions)) J = _N*2 _thetas = [np.arccos( (2.*j - 1. - J)/J) for j in range(1,J+1)] _phis = [ np.sqrt( np.pi*J) *np.arcsin( (2.*j - 1. - J)/J ) for j in range(1,J+1)] _x = np.sin(_thetas)*np.cos(_phis) _y = np.sin(_thetas)*np.sin(_phis) _z = np.cos(_thetas) nn = int(_N * 1.01) _u_vecs = np.array(zip(_x,_y,_z))[-nn:] rec_pts = np.array([self.reciprocal_lattice_points[i] for i in range(len(self.reciprocal_lattice_points))]) N_unique = len(unique_cell_dimensions) # much faster to use numpy for massively over-sampled hemisphere.. func_vals = np.zeros( nn*N_unique) vecs = np.zeros( (nn*N_unique, 3) ) for i, l in enumerate(unique_cell_dimensions): # create noise model on top of lattice lengths... if basis_vec_noise: vec_mag = np.random.normal( l, scale=noise_scale, size=_u_vecs.shape[0] ) vec_mag = vec_mag[:,None] else: vec_mag = l ul = _u_vecs * vec_mag func_slc = slice( i*nn, (i+1)*nn) vecs[func_slc] = ul func_vals[func_slc] = np.sum( np.cos( 2*np.pi*np.dot(rec_pts, ul.T) ), axis=0) order = np.argsort(func_vals)[::-1] # sort function values, largest values first function_values = func_vals[order] vectors = vecs[order] else: # fall back on original flex method vectors = flex.vec3_double() function_values = flex.double() print "Number of search vectors: %i" % ( len(SST.angles)* len(unique_cell_dimensions)) for i, direction in enumerate(SST.angles): for l in unique_cell_dimensions: v = matrix.col(direction.dvec) * l f = compute_functional(v.elems) vectors.append(v.elems) function_values.append(f) perm = flex.sort_permutation(function_values, reverse=True) vectors = vectors.select(perm) function_values = function_values.select(perm) print("made search vecs") unique_vectors = [] i = 0 while len(unique_vectors) < N_UNIQUE_V: v = matrix.col(vectors[i]) is_unique = True if i > 0: for v_u in unique_vectors: if v.length() < v_u.length(): if is_approximate_integer_multiple(v, v_u): is_unique = False break elif is_approximate_integer_multiple(v_u, v): is_unique = False break if is_unique: unique_vectors.append(v) i += 1 print ("chose unique basis vecs") if self.params.debug: for i in range(N_UNIQUE_V): v = matrix.col(vectors[i]) print v.elems, v.length(), function_values[i] basis_vectors = [v.elems for v in unique_vectors] self.candidate_basis_vectors = basis_vectors if self.params.optimise_initial_basis_vectors: self.params.optimize_initial_basis_vectors = False # todo: verify this reference to self.reciprocal_lattice_points is correct optimised_basis_vectors = optimise_basis_vectors( self.reciprocal_lattice_points, basis_vectors) optimised_function_values = flex.double([ compute_functional(v) for v in optimised_basis_vectors]) perm = flex.sort_permutation(optimised_function_values, reverse=True) optimised_basis_vectors = optimised_basis_vectors.select(perm) optimised_function_values = optimised_function_values.select(perm) unique_vectors = [matrix.col(v) for v in optimised_basis_vectors] print "Number of unique vectors: %i" %len(unique_vectors) if self.params.debug: for i in range(len(unique_vectors)): print compute_functional(unique_vectors[i].elems), unique_vectors[i].length(), unique_vectors[i].elems print crystal_models = [] self.candidate_basis_vectors = unique_vectors if self.params.debug: self.debug_show_candidate_basis_vectors() if self.params.debug_plots: self.debug_plot_candidate_basis_vectors() candidate_orientation_matrices \ = self.find_candidate_orientation_matrices( unique_vectors) # max_combinations=self.params.basis_vector_combinations.max_try) FILTER_BY_MAG = True if FILTER_BY_MAG: print("\n\n FILTERING BY MAG\n\n") FILTER_TOL = 10,3 # within 5 percent of params and 1 percent of ang target_uc = self.params.known_symmetry.unit_cell.parameters() good_mats = [] for c in candidate_orientation_matrices: uc = c.get_unit_cell().parameters() comps = [] for i in range(3): tol = 0.01* FILTER_TOL[0] * target_uc[i] low = target_uc[i] - tol/2. high = target_uc[i] + tol/2 comps.append( low < uc[i] < high ) for i in range(3,6): low = target_uc[i] - FILTER_TOL[1] high = target_uc[i] + FILTER_TOL[1] comps.append( low < uc[i] < high ) if all( comps): print("matrix is ok:", c) good_mats.append(c) print("\nFilter kept %d / %d mats" % \ (len(good_mats), len(candidate_orientation_matrices))) candidate_orientation_matrices = good_mats crystal_model, n_indexed = self.choose_best_orientation_matrix( candidate_orientation_matrices) orange = 2 if crystal_model is not None: crystal_models = [crystal_model] else: crystal_models = [] #assert len(crystal_models) > 0 candidate_orientation_matrices = crystal_models #for i in range(len(candidate_orientation_matrices)): #if self.target_symmetry_primitive is not None: ##print "symmetrizing model" ##self.target_symmetry_primitive.show_summary() #symmetrized_model = self.apply_symmetry( #candidate_orientation_matrices[i], self.target_symmetry_primitive) #candidate_orientation_matrices[i] = symmetrized_model self.candidate_crystal_models = candidate_orientation_matrices # memory leak somewhere... probably not here.. but just in case... del _x, _y, _z, _u_vecs, order, rec_pts, vecs, func_vals, vectors, function_values
def get_finegrained_SST(self, coarse_sampling_grid=0.005): d_min = self.params.refinement_protocol.d_min_start sel = self.reflections["id"] == -1 if d_min is not None: sel &= 1 / self.reflections["rlp"].norms() > d_min reciprocal_lattice_points = self.reflections["rlp"].select(sel) print("Indexing from %i reflections COARSE" % len(reciprocal_lattice_points)) from rstbx.array_family import flex from rstbx.dps_core import SimpleSamplerTool assert self.target_symmetry_primitive is not None assert self.target_symmetry_primitive.unit_cell() is not None SST = SimpleSamplerTool(coarse_sampling_grid) SST.construct_hemisphere_grid(SST.incr) cell_dimensions = self.target_symmetry_primitive.unit_cell( ).parameters()[:3] unique_cell_dimensions = set(cell_dimensions) print("Number of search vectors COARSE: %i" % (len(SST.angles) * len(unique_cell_dimensions))) vectors = flex.vec3_double() function_values = flex.double() import time time1 = time.time() SST_all_angles = flex.Direction() for i, direction in enumerate(SST.angles): for l in unique_cell_dimensions: v = matrix.col(direction.dvec) * l f = compute_functional(v.elems, reciprocal_lattice_points) vectors.append(v.elems) function_values.append(f) SST_all_angles.append(direction) time2 = time.time() print('COARSE GRID SEARCH TIME=', time2 - time1) perm = flex.sort_permutation(function_values, reverse=True) vectors = vectors.select(perm) function_values = function_values.select(perm) unique_vectors = [] unique_indices = [] i = 0 while len(unique_vectors) < 30: v = matrix.col(vectors[i]) is_unique = True if i > 0: for v_u in unique_vectors: if v.length() < v_u.length(): if is_approximate_integer_multiple( v, v_u, relative_tolerance=0.2, angular_tolerance=5.0): is_unique = False break elif is_approximate_integer_multiple( v_u, v, relative_tolerance=0.2, angular_tolerance=5.0): is_unique = False break if is_unique: unique_vectors.append(v) unique_indices.append(perm[i]) i += 1 # Evaluate which SST angles contributed to the unique vectors SST_filter = flex.Direction() for v in unique_indices: direction = SST.angles[v // len(unique_cell_dimensions)] SST_filter.append(direction) SST.construct_hemisphere_grid_finegrained(0.0001, coarse_sampling_grid, SST_filter) return SST
def real_space_grid_smart_search(self): """ overloading the real_space_grid_search method to use a sparser SST with highly fine grid after initially doing a sparse grid to evaluate grid points of interest""" d_min = self.params.refinement_protocol.d_min_start sel = self.reflections["id"] == -1 if d_min is not None: sel &= 1 / self.reflections["rlp"].norms() > d_min reciprocal_lattice_points = self.reflections["rlp"].select(sel) print("Indexing from %i reflections FINE " % len(reciprocal_lattice_points)) from rstbx.array_family import flex from rstbx.dps_core import SimpleSamplerTool assert self.target_symmetry_primitive is not None assert self.target_symmetry_primitive.unit_cell() is not None SST = self.get_finegrained_SST() cell_dimensions = self.target_symmetry_primitive.unit_cell( ).parameters()[:3] unique_cell_dimensions = set(cell_dimensions) print("Number of search vectors FINE : %i" % (len(SST.finegrained_angles) * len(unique_cell_dimensions))) vectors = flex.vec3_double() function_values = flex.double() # Do a search within the cluster of coarse grids and only return the top few values ? find_max_within_cluster = True import time time1 = time.time() if find_max_within_cluster: top_n_values = 1 # number of top scoring vectors to return in each coarse grid per unique dimension for count in range(len(SST.n_entries_finegrained[:-1])): start = SST.n_entries_finegrained[count] end = SST.n_entries_finegrained[count + 1] for l in unique_cell_dimensions: tmp_vectors = flex.vec3_double() tmp_function_values = flex.double() for i, direction in enumerate( SST.finegrained_angles[start:end]): v = matrix.col(direction.dvec) * l f = compute_functional(v.elems, reciprocal_lattice_points) tmp_vectors.append(v.elems) tmp_function_values.append(f) perm = flex.sort_permutation(tmp_function_values, reverse=True) tmp_vectors = tmp_vectors.select(perm)[0:top_n_values] tmp_function_values = tmp_function_values.select( perm)[0:top_n_values] vectors.extend(tmp_vectors) function_values.extend(tmp_function_values) else: for i, direction in enumerate(SST.finegrained_angles): for l in unique_cell_dimensions: v = matrix.col(direction.dvec) * l f = compute_functional(v.elems, reciprocal_lattice_points) vectors.append(v.elems) function_values.append(f) time2 = time.time() print('FINE GRID SEARCH TIME=', time2 - time1) perm = flex.sort_permutation(function_values, reverse=True) vectors = vectors.select(perm) function_values = function_values.select(perm) unique_vectors = [] unique_indices = [] i = 0 time1 = time.time() while len(unique_vectors) < 30: v = matrix.col(vectors[i]) is_unique = True if i > 0: for v_u in unique_vectors: if v.length() < v_u.length(): if is_approximate_integer_multiple( v, v_u, relative_tolerance=0.2, angular_tolerance=5.0): is_unique = False break elif is_approximate_integer_multiple( v_u, v, relative_tolerance=0.2, angular_tolerance=5.0): is_unique = False break if is_unique: unique_vectors.append(v) unique_indices.append(perm[i]) i += 1 time2 = time.time() print('FINE GRID UNIQUE VECTOR SEARCH TIME = ', time2 - time1) #from IPython import embed; embed(); exit() # FIXME debugging here #for direction in SST.finegrained_angles: # print ('DEBUGGGG = ', direction.phi, direction.psi) basis_vectors = [v.elems for v in unique_vectors] self.candidate_basis_vectors = basis_vectors logger.info("Number of unique vectors: %i" % len(unique_vectors)) for i in range(len(unique_vectors)): logger.debug("%s %s %s" % (str( compute_functional(unique_vectors[i].elems, reciprocal_lattice_points)), str(unique_vectors[i].length()), str(unique_vectors[i].elems))) crystal_models = [] self.candidate_basis_vectors = unique_vectors self.debug_show_candidate_basis_vectors() candidate_orientation_matrices = self.find_candidate_orientation_matrices( unique_vectors) crystal_model, n_indexed = self.choose_best_orientation_matrix( candidate_orientation_matrices) if crystal_model is not None: crystal_models = [crystal_model] else: crystal_models = [] candidate_orientation_matrices = crystal_models self.candidate_crystal_models = candidate_orientation_matrices
def real_space_grid_search(self): d_min = self.params.refinement_protocol.d_min_start sel = (self.reflections['id'] == -1) if d_min is not None: sel &= (1 / self.reflections['rlp'].norms() > d_min) reciprocal_lattice_points = self.reflections['rlp'].select(sel) logger.info("Indexing from %i reflections" % len(reciprocal_lattice_points)) def compute_functional(vector): two_pi_S_dot_v = 2 * math.pi * reciprocal_lattice_points.dot( vector) return flex.sum(flex.cos(two_pi_S_dot_v)) from rstbx.array_family import flex from rstbx.dps_core import SimpleSamplerTool assert self.target_symmetry_primitive is not None assert self.target_symmetry_primitive.unit_cell() is not None SST = SimpleSamplerTool( self.params.real_space_grid_search.characteristic_grid) SST.construct_hemisphere_grid(SST.incr) cell_dimensions = self.target_symmetry_primitive.unit_cell( ).parameters()[:3] unique_cell_dimensions = set(cell_dimensions) logger.info("Number of search vectors: %i" % (len(SST.angles) * len(unique_cell_dimensions))) vectors = flex.vec3_double() function_values = flex.double() for i, direction in enumerate(SST.angles): for l in unique_cell_dimensions: v = matrix.col(direction.dvec) * l f = compute_functional(v.elems) vectors.append(v.elems) function_values.append(f) perm = flex.sort_permutation(function_values, reverse=True) vectors = vectors.select(perm) function_values = function_values.select(perm) unique_vectors = [] i = 0 while len(unique_vectors) < 30: v = matrix.col(vectors[i]) is_unique = True if i > 0: for v_u in unique_vectors: if v.length() < v_u.length(): if is_approximate_integer_multiple(v, v_u): is_unique = False break elif is_approximate_integer_multiple(v_u, v): is_unique = False break if is_unique: unique_vectors.append(v) i += 1 for i in range(30): v = matrix.col(vectors[i]) logger.debug( "%s %s %s" % (str(v.elems), str(v.length()), str(function_values[i]))) basis_vectors = [v.elems for v in unique_vectors] self.candidate_basis_vectors = basis_vectors if self.params.optimise_initial_basis_vectors: optimised_basis_vectors = optimise_basis_vectors( reciprocal_lattice_points, basis_vectors) optimised_function_values = flex.double( [compute_functional(v) for v in optimised_basis_vectors]) perm = flex.sort_permutation(optimised_function_values, reverse=True) optimised_basis_vectors = optimised_basis_vectors.select(perm) optimised_function_values = optimised_function_values.select(perm) unique_vectors = [matrix.col(v) for v in optimised_basis_vectors] logger.info("Number of unique vectors: %i" % len(unique_vectors)) for i in range(len(unique_vectors)): logger.debug( "%s %s %s" % (str(compute_functional( unique_vectors[i].elems)), str(unique_vectors[i].length()), str(unique_vectors[i].elems))) crystal_models = [] self.candidate_basis_vectors = unique_vectors self.debug_show_candidate_basis_vectors() if self.params.debug_plots: self.debug_plot_candidate_basis_vectors() candidate_orientation_matrices \ = self.find_candidate_orientation_matrices( unique_vectors, max_combinations=self.params.basis_vector_combinations.max_try) crystal_model, n_indexed = self.choose_best_orientation_matrix( candidate_orientation_matrices) if crystal_model is not None: crystal_models = [crystal_model] else: crystal_models = [] #assert len(crystal_models) > 0 candidate_orientation_matrices = crystal_models #for i in range(len(candidate_orientation_matrices)): #if self.target_symmetry_primitive is not None: ##print "symmetrizing model" ##self.target_symmetry_primitive.show_summary() #symmetrized_model = self.apply_symmetry( #candidate_orientation_matrices[i], self.target_symmetry_primitive) #candidate_orientation_matrices[i] = symmetrized_model self.candidate_crystal_models = candidate_orientation_matrices
def find_basis_vector_combinations_cluster_analysis(self): # hijack the xray.structure class to facilitate calculation of distances xs = xray.structure(crystal_symmetry=self.crystal_symmetry) for i, site in enumerate(self.sites): xs.add_scatterer(xray.scatterer("C%i" %i, site=site)) xs = xs.sites_mod_short() xs = xs.select(xs.sites_frac().norms() < 0.45) cell_multiplier = 10 xs1 = xs.customized_copy( unit_cell=uctbx.unit_cell([xs.unit_cell().parameters()[0]*cell_multiplier]*3)) xs1.set_sites_cart(xs.sites_cart()) xs = xs1 sites_cart = xs.sites_cart() lengths = flex.double([matrix.col(sc).length() for sc in sites_cart]) xs = xs.select(flex.sort_permutation(lengths)) if self.params.debug: with open('peaks.pdb', 'wb') as f: print >> f, xs.as_pdb_file() vector_heights = flex.double() sites_frac = xs.sites_frac() pair_asu_table = xs.pair_asu_table(distance_cutoff=self.params.max_cell) asu_mappings = pair_asu_table.asu_mappings() distances = crystal.calculate_distances(pair_asu_table, sites_frac) vectors = [] difference_vectors = [] pairs = [] for di in distances: if di.distance < self.params.min_cell: continue i_seq, j_seq = di.i_seq, di.j_seq if i_seq > j_seq: continue pairs.append((i_seq, j_seq)) rt_mx_ji = di.rt_mx_ji site_frac_ji = rt_mx_ji * sites_frac[j_seq] site_cart_ji = xs.unit_cell().orthogonalize(site_frac_ji) site_cart_i = xs.unit_cell().orthogonalize(sites_frac[i_seq]) vectors.append(matrix.col(site_cart_ji)) diff_vec = matrix.col(site_cart_i) - matrix.col(site_cart_ji) if diff_vec[0] < 0: # only one hemisphere of difference vector space diff_vec = -diff_vec difference_vectors.append(diff_vec) params = self.params.multiple_lattice_search.cluster_analysis if params.method == 'dbscan': i_cluster = self.cluster_analysis_dbscan(difference_vectors) min_cluster_size = 1 elif params.method == 'hcluster': i_cluster = self.cluster_analysis_hcluster(difference_vectors) i_cluster -= 1 # hcluster starts counting at 1 min_cluster_size = params.min_cluster_size if self.params.debug_plots: self.debug_plot_clusters( difference_vectors, i_cluster, min_cluster_size=min_cluster_size) clusters = [] min_cluster_size = params.min_cluster_size for i in range(max(i_cluster)+1): isel = (i_cluster == i).iselection() if len(isel) < min_cluster_size: continue clusters.append(isel) cluster_point_sets = [] centroids = [] cluster_sizes = flex.int() difference_vectors = flex.vec3_double(difference_vectors) from libtbx.utils import flat_list for cluster in clusters: points = flat_list([pairs[i] for i in cluster]) cluster_point_sets.append(set(points)) d_vectors = difference_vectors.select(cluster) cluster_sizes.append(len(d_vectors)) centroids.append(d_vectors.mean()) # build a graph where each node is a centroid from the difference vector # cluster analysis above, and an edge is defined when there is a # significant overlap between the sets of peaks in the FFT map that # contributed to the difference vectors in two clusters import networkx as nx G = nx.Graph() G.add_nodes_from(range(len(cluster_point_sets))) cutoff_frac = 0.25 for i in range(len(cluster_point_sets)): for j in range(i+1, len(cluster_point_sets)): intersection_ij = cluster_point_sets[i].intersection( cluster_point_sets[j]) union_ij = cluster_point_sets[i].union(cluster_point_sets[j]) frac_connected = len(intersection_ij)/len(union_ij) if frac_connected > cutoff_frac: G.add_edge(i, j) # iteratively find the maximum cliques in the graph # break from the loop if there are no cliques remaining or there are # fewer than 3 vectors in the remaining maximum clique # Allow 1 basis vector to be shared between two cliques, to allow for # cases where two lattices share one basis vectors (e.g. two plate # crystals exactly aligned in one direction, but not in the other two) distinct_cliques = [] cliques = list(nx.find_cliques(G)) cliques = sorted(cliques, key=len, reverse=True) for i, clique in enumerate(cliques): clique = set(clique) if len(clique) < 3: break is_distinct = True for c in distinct_cliques: if len(c.intersection(clique)) > 1: is_distinct = False break if is_distinct: distinct_cliques.append(clique) this_set = set() for i_cluster in clique: this_set = this_set.union(cluster_point_sets[i_cluster]) logger.info("Clique %i: %i lattice points" %(i+1, len(this_set))) assert len(distinct_cliques) > 0 logger.info("Estimated number of lattices: %i" %len(distinct_cliques)) self.candidate_basis_vectors = [] self.candidate_crystal_models = [] for clique in distinct_cliques: sel = flex.size_t(list(clique)) vectors = flex.vec3_double(centroids).select(sel) perm = flex.sort_permutation(vectors.norms()) vectors = [matrix.col(vectors[p]) for p in perm] # exclude vectors that are (approximately) integer multiples of a shorter # vector unique_vectors = [] for v in vectors: is_unique = True for v_u in unique_vectors: if is_approximate_integer_multiple(v_u, v, relative_tolerance=0.01, angular_tolerance=0.5): is_unique = False break if is_unique: unique_vectors.append(v) vectors = unique_vectors self.candidate_basis_vectors.extend(vectors) candidate_orientation_matrices \ = self.find_candidate_orientation_matrices( vectors, max_combinations=self.params.basis_vector_combinations.max_try) if len(candidate_orientation_matrices) == 0: continue crystal_model, n_indexed = self.choose_best_orientation_matrix( candidate_orientation_matrices) if crystal_model is None: continue # map to minimum reduced cell crystal_symmetry = crystal.symmetry( unit_cell=crystal_model.get_unit_cell(), space_group=crystal_model.get_space_group()) cb_op = crystal_symmetry.change_of_basis_op_to_minimum_cell() crystal_model = crystal_model.change_basis(cb_op) self.candidate_crystal_models.append(crystal_model) if self.params.debug: file_name = "vectors.pdb" a = self.params.max_cell cs = crystal.symmetry(unit_cell=(a,a,a,90,90,90), space_group="P1") xs = xray.structure(crystal_symmetry=cs) for v in difference_vectors: v = matrix.col(v) xs.add_scatterer(xray.scatterer("C", site=v/(a/10))) xs.sites_mod_short() with open(file_name, 'wb') as f: print >> f, xs.as_pdb_file() for crystal_model in self.candidate_crystal_models: logger.debug(crystal_model)
def find_candidate_basis_vectors(self): # hijack the xray.structure class to facilitate calculation of distances xs = xray.structure(crystal_symmetry=self.crystal_symmetry) for i, site in enumerate(self.sites): xs.add_scatterer(xray.scatterer("C%i" %i, site=site)) xs = xs.sites_mod_short() sites_cart = xs.sites_cart() lengths = flex.double([matrix.col(sc).length() for sc in sites_cart]) perm = flex.sort_permutation(lengths) xs = xs.select(perm) volumes = self.volumes.select(perm) if self.params.debug: with open('peaks.pdb', 'wb') as f: print >> f, xs.as_pdb_file() sites_frac = xs.sites_frac() vectors = xs.sites_cart() norms = vectors.norms() sel = (norms > self.params.min_cell) & (norms < (2 * self.params.max_cell)) vectors = vectors.select(sel) vectors = [matrix.col(v) for v in vectors] volumes = volumes.select(sel) # XXX loop over these vectors and sort into groups similar to further down # group similar angle and lengths, also catch integer multiples of vectors vector_groups = [] relative_length_tolerance = 0.1 angle_tolerance = 5 # degrees orth = self.fft_cell.orthogonalize for v, volume in zip(vectors, volumes): length = v.length() if length < self.params.min_cell or length > (2 * self.params.max_cell): continue matched_group = False for group in vector_groups: mean_v = group.mean() mean_v_length = mean_v.length() if (abs(mean_v_length - length)/max(mean_v_length, length) < relative_length_tolerance): angle = mean_v.angle(v, deg=True) if angle < angle_tolerance: group.append(v, length, volume) matched_group = True break elif abs(180-angle) < angle_tolerance: group.append(-v, length, volume) matched_group = True break if not matched_group: group = vector_group() group.append(v, length, volume) vector_groups.append(group) vectors = [g.mean() for g in vector_groups] volumes = flex.double(max(g.volumes) for g in vector_groups) ## sort by length #lengths = flex.double([v.length() for v in vectors]) #perm = flex.sort_permutation(lengths) # sort by peak size perm = flex.sort_permutation(volumes, reverse=True) volumes = volumes.select(perm) vectors = [vectors[i] for i in perm] for i, (v, volume) in enumerate(zip(vectors, volumes)): logger.debug("%s %s %s" %(i, v.length(), volume)) lengths = flex.double(v.length() for v in vectors) perm = flex.sort_permutation(lengths) # exclude vectors that are (approximately) integer multiples of a shorter # vector unique_vectors = [] unique_volumes = flex.double() for p in perm: v = vectors[p] is_unique = True for i, v_u in enumerate(unique_vectors): if ((unique_volumes[i] > volumes[p]) and is_approximate_integer_multiple(v_u, v)): logger.debug("rejecting %s: integer multiple of %s" %(v.length(), v_u.length())) is_unique = False break if is_unique: unique_vectors.append(v) unique_volumes.append(volumes[p]) # re-sort by peak volume perm = flex.sort_permutation(unique_volumes, reverse=True) vectors = [unique_vectors[i] for i in perm] volumes = unique_volumes.select(perm) #for i, (v, volume) in enumerate(zip(vectors, volumes)): #logger.debug("%s %s %s" %(i, v.length(), volume)) self.candidate_basis_vectors = vectors