def _get_sites_to_merge(self, st, coordinating_mask = None): sn = st.site_network # -- Compute jump statistics if not sn.has_attribute('n_ij'): ja = JumpAnalysis() ja.run(st) pos = sn.centers if coordinating_mask is None: coordinating_mask = sn.static_mask else: assert not np.any(coordinating_mask & sn.mobile_mask) # -- Build images mobile_idex = np.where(sn.mobile_mask)[0][0] one_mobile_structure = sn.structure[coordinating_mask] one_mobile_structure.extend(sn.structure[mobile_idex]) mobile_idex = -1 one_mobile_structure.set_calculator(self.calculator) interpolation_coeffs = np.linspace(0, 1, self.n_driven_images) energies = np.empty(shape = self.n_driven_images) # -- Decide on pairs to check pbcc = PBCCalculator(sn.structure.cell) dists = pbcc.pairwise_distances(pos) # At the start, all within distance cutoff are mergable mergable = dists <= self.maximum_pairwise_distance mergable &= sn.n_ij >= self.minimum_jumps_mergable # -- Check pairs' barriers # Symmetric, and diagonal is trivially true. Combinations avoids those cases. jbuf = pos[0].copy() first_calculate = True mergable_pairs = (p for p in itertools.combinations(range(sn.n_sites), r = 2) if mergable[p] or mergable[p[1], p[0]]) n_mergable = (np.sum(mergable) - sn.n_sites) // 2 for i, j in tqdm(mergable_pairs, total = n_mergable): jbuf[:] = pos[j] # Get minimage _ = pbcc.min_image(pos[i], jbuf) # Do coordinate driving vector = jbuf - pos[i] for image_i in range(self.n_driven_images): one_mobile_structure.positions[mobile_idex] = vector one_mobile_structure.positions[mobile_idex] *= interpolation_coeffs[image_i] one_mobile_structure.positions[mobile_idex] += pos[i] energies[image_i] = one_mobile_structure.get_potential_energy() first_calculate = False # Check barrier barrier_idex = np.argmax(energies) forward_barrier = energies[barrier_idex] - energies[0] backward_barrier = energies[barrier_idex] - energies[-1] # If it's an actual maxima barrier between them, then we want to # check its height if barrier_idex != 0 and barrier_idex != self.n_driven_images - 1: mergable[i, j] = forward_barrier <= self.barrier_threshold mergable[j, i] = backward_barrier <= self.barrier_threshold # Otherwise, if there's no maxima between them, they are in the same # basin. # Get mergable groups n_merged_sites, labels = connected_components( mergable, directed = True, connection = 'strong' ) # MergeSites will check pairwise distances; we just need to make it the # right format. merge_groups = [] for lbl in range(n_merged_sites): merge_groups.append(np.where(labels == lbl)[0]) return merge_groups
def _plot_edges(self, sn, ax = None, *args, **kwargs): if not 'intensity' in self.edge_mappings: return [] pbcc = PBCCalculator(sn.structure.cell) n_sites = sn.n_sites centers = sn.centers # -- Edge attributes all_cs = None all_linewidths = None all_color = None all_groups = None # Get value arrays as they exist for edgekey in self.edge_mappings: edgeval = getattr(sn, self.edge_mappings[edgekey]) if edgekey == 'intensity': all_cs = edgeval.copy() elif edgekey == 'width': all_linewidths = edgeval.copy() elif edgekey == 'group': assert edgeval.dtype == np.int all_groups = edgeval else: raise KeyError("Invalid edge mapping key `%s`" % edgekey) do_widths = not all_linewidths is None do_groups = not all_groups is None # - Normalize # Ignore values on the diagonal since we ignore them in the loop diag_mask = np.ones(shape = all_cs.shape, dtype = np.bool) np.fill_diagonal(diag_mask, False) self._normalize(all_cs, diag_mask) if do_widths: self._normalize(all_linewidths, diag_mask) # -- Construct Line3DCollection segments # Whether an edge has already been added done_already = np.zeros(shape = (n_sites, n_sites), dtype = np.bool) # For the Line3DCollection segments = [] cs = [] linewidths = [] groups = [] # To plot minimum images that are outside unit cell sites_to_plot = [] sites_to_plot_positions = [] for i in range(n_sites): for j in range(n_sites): # No self edges if i == j: continue # If was already done if done_already[i, j]: continue # Ignore anything below the threshold if all_cs[i, j] <= self.min_color_threshold: continue if do_widths and all_linewidths[i, j] <= self.min_width_threshold: continue segment = np.empty(shape = (2, 3), dtype = centers.dtype) segment[0] = centers[i] ptbuf = centers[j].copy() # Modified segment[1] in place minimg = pbcc.min_image(segment[0], ptbuf) was_already_min_img = minimg == 111 segment[1] = ptbuf segments.append(segment) # If they are eachother's minimum image, then don't bother plotting # j -> i if was_already_min_img: done_already[j, i] = True else: # We'll plot it sites_to_plot.append(j) sites_to_plot_positions.append(segment[1]) # The mean cs.append(np.mean([all_cs[i, j], all_cs[j, i]])) if do_widths: linewidths.append(np.mean([all_linewidths[i, j], all_linewidths[j, i]])) if do_groups: # Assumes symmetric groups.append(all_groups[i, j]) done_already[i, j] = True # -- Construct final Line3DCollection assert len(cs) == len(segments) if len(cs) > 0: lccolors = np.empty(shape = (len(cs), 4), dtype = np.float) # Group colors if do_groups: for i in range(len(cs)): if groups[i] >= len(SiteNetworkPlotter.EDGE_GROUP_COLORS) - 1: raise ValueError("Too many groups, not enough group colors") lccolors[i] = matplotlib.colors.to_rgba(SiteNetworkPlotter.EDGE_GROUP_COLORS[groups[i]]) else: lccolors[:] = matplotlib.colors.to_rgba(SiteNetworkPlotter.EDGE_GROUP_COLORS[0]) # Intensity alpha lccolors[:,3] = np.array(cs) * self.minmax_edge_alpha[1] lccolors[:,3] += self.minmax_edge_alpha[0] if do_widths: linewidths = np.asarray(linewidths) linewidths *= self.minmax_linewidth[1] linewidths += self.minmax_linewidth[0] else: linewidths = self.minmax_linewidth[1] * 0.5 lc = Line3DCollection(segments, linewidths = linewidths, colors = lccolors, zorder = -20) ax.add_collection(lc) # -- Plot new sites if len(sites_to_plot) > 0: sn2 = sn[sites_to_plot] sn2.update_centers(np.asarray(sites_to_plot_positions)) pts_params = dict(self.plot_points_params) pts_params['alpha'] = 0.2 return self._site_layers(sn2, pts_params, same_normalization = True) else: return [] else: return []
def _build_mic_connmat(self, sn, connectivity_matrix): # We use a 3x3x3 = 27 supercell, so there are 27x as many sites assert len(sn) == connectivity_matrix.shape[0] images = np.asarray(list(itertools.product(range(-1, 2), repeat=3))) image_to_idex = dict( (100 * (image[0] + 1) + 10 * (image[1] + 1) + (image[2] + 1), i) for i, image in enumerate(images)) n_images = len(images) assert n_images == 27 n_sites = len(sn) pos = sn.centers #.copy() # TODO: copy not needed after reinstall of sitator! n_total_sites = len(images) * n_sites newmat = lil_matrix((n_total_sites, n_total_sites), dtype=np.bool) mask_000 = np.zeros(shape=n_total_sites, dtype=np.bool) index_000 = image_to_idex[111] mask_000[index_000:index_000 + n_sites] = True assert np.sum(mask_000) == len(sn) pbcc = PBCCalculator(sn.structure.cell) buf = np.empty(shape=3) internal_mat = np.zeros_like(connectivity_matrix) external_connections = [] for from_site, to_site in zip(*np.where(connectivity_matrix)): buf[:] = pos[to_site] if pbcc.min_image(pos[from_site], buf) == 111: # If we're in the main image, keep the connection: it's internal internal_mat[from_site, to_site] = True #internal_mat[to_site, from_site] = True # fake FIXME else: external_connections.append((from_site, to_site)) #external_connections.append((to_site, from_site)) # FAKE FIXME for image_idex, image in enumerate(images): # Make the block diagonal newmat[image_idex * n_sites:(image_idex + 1) * n_sites, image_idex * n_sites:(image_idex + 1) * n_sites] = internal_mat # Check all external connections from this image; add other sparse entries for from_site, to_site in external_connections: buf[:] = pos[to_site] to_mic = pbcc.min_image(pos[from_site], buf) to_in_image = image + [ (to_mic // 10**(2 - i) % 10) - 1 for i in range(3) ] # FIXME: is the -1 right assert to_in_image is not None, "%s" % to_in_image assert np.max(np.abs(to_in_image)) <= 2 if not np.any(np.abs(to_in_image) > 1): to_in_image = 100 * (to_in_image[0] + 1) + 10 * ( to_in_image[1] + 1) + 1 * (to_in_image[2] + 1) newmat[image_idex * n_sites + from_site, image_to_idex[to_in_image] * n_sites + to_site] = True assert np.sum(newmat) >= n_images * np.sum( internal_mat) # Lowest it can be is if every one is internal return newmat, mask_000, images