def __call__(self, x1, x2): if self.align_com: x1 = CoMToOrigin(x1).reshape([-1, 3]) x2 = CoMToOrigin(x2).reshape([-1, 3]) # calculate distance of all atoms R1 = np.sqrt(np.sum(x1 * x1, axis=1)) R2 = np.sqrt(np.sum(x2 * x2, axis=1)) # at least 2 atoms are needed # get atom most outer atom # get 1. reference atom in configuration 1 # use the atom with biggest distance to com idx_sorted = R1.argsort() idx1_1 = idx_sorted[-1] # find second atom which is not in a line for idx1_2 in reversed(idx_sorted[0:-1]): # stop if angle is larger than threshold cos_theta1 = np.dot(x1[idx1_1], x1[idx1_2]) / (np.linalg.norm(x1[idx1_1]) * np.linalg.norm(x1[idx1_2])) if cos_theta1 < 0.9: break # do a very quick check if most distant atom from # center are within accuracy if np.abs(R1[idx1_1] - R2.max()) > self.accuracy: return False # get indices of atoms in shell of thickness 2*accuracy candidates1 = np.arange(len(R2))[(R2 > R1[idx1_1] - self.accuracy) * (R2 < R1[idx1_1] + self.accuracy)] candidates2 = np.arange(len(R2))[(R2 > R1[idx1_2] - self.accuracy) * (R2 < R1[idx1_2] + self.accuracy)] # now loop over all combinations for idx2_1 in candidates1: for idx2_2 in candidates2: if idx2_1 == idx2_2: continue # we can immediately trash the match if angle does not match cos_theta2 = np.dot(x2[idx2_1], x2[idx2_2]) / (np.linalg.norm(x2[idx2_1]) * np.linalg.norm(x2[idx2_2])) if np.abs(cos_theta2 - cos_theta2) > 0.5: continue # get rotation for current atom match candidates rot = rmsfit.findrotation_kabsch(x1[[idx1_1, idx1_2]], x2[[idx2_1, idx2_2]], False) # pass on the match for a closer check if self.check_match(x1, x2, rot, 1.0): return True if self.check_inversion: # get rotation for current atom match candidates rot = rmsfit.findrotation_kabsch(x1[[idx1_1, idx1_2]], -x2[[idx2_1, idx2_2]], False) # pass on the match for a closer check if self.check_match(x1, -x2, rot, -1.0): return True return False
def check_match(self, x1, x2, rot, inverse): """ Make a more detailed comparison if the 2 structures match Parameters ---------- x1: np.array coordinates of structure 1 x2: np.array coordinates of structure 2 rot: np.array, 3x3 guessed rotation based on reference atoms inverse: double -1.0 if do match for inverted coordinates, 1.0 otherwise returns: boolean True or False for match """ # apply the rotation x1_trial = np.dot(rot, x1.transpose()).transpose() # make a copy since findBestPermutation will mess up order x2_trial = x2.copy() # get the best permutation dist, x1n, x2n = findBestPermutation(x1_trial.flatten(), x2_trial.flatten()) x1n = x1n.reshape([-1, 3]) x2n = x2n.reshape([-1, 3]) # x1n = x1_trial # x2n = x2_trial # now find best rotational alignment, this is more reliable than just # aligning the 2 reference atoms rot2 = rmsfit.findrotation_kabsch(x1n, x2n) x1n = np.dot(rot2, x1n.transpose()).transpose() # use the maximum distance, not rms as cutoff criterion max_dist = np.sqrt(np.sum((x1n - x2n) * (x1n - x2n), axis=1)).max() if max_dist < self.accuracy: self.best_rotation = np.dot(rot2, rot) return True return False