def score(self, segpos, *, verbosity=False, **kw): x_from = segpos[self.from_seg] x_to = segpos[self.to_seg] xhat = x_to @ inv(x_from) trans = xhat[..., :, 3] if self.nfold is 1: angle = hm.angle_of(xhat) carterrsq = np.sum(trans[..., :3]**2, axis=-1) roterrsq = angle**2 else: if self.origin_seg is not None: tgtaxis = segpos[self.origin_seg] @ [0, 0, 1, 0] tgtcen = segpos[self.origin_seg] @ [0, 0, 0, 1] axis, angle, cen = hm.axis_ang_cen_of(xhat) carterrsq = hm.hnorm2(cen - tgtcen) roterrsq = (1 - np.abs(hm.hdot(axis, tgtaxis))) * np.pi else: # much cheaper if cen not needed axis, angle = hm.axis_angle_of(xhat) carterrsq = roterrsq = 0 carterrsq = carterrsq + hm.hdot(trans, axis)**2 roterrsq = roterrsq + (angle - self.symangle)**2 # if self.relweight is not None: # # penalize 'relative' error # distsq = np.sum(trans[..., :3]**2, axis=-1) # relerrsq = carterrsq / distsq # relerrsq[np.isnan(relerrsq)] = 9e9 # # too much of a hack?? # carterrsq += self.relweight * relerrsq if verbosity > 0: print('axis', axis[0]) print('trans', trans[0]) print('dot trans', hm.hdot(trans, axis)[0]) print('angle', angle[0] * 180 / np.pi) return np.sqrt(carterrsq / self.tol**2 + roterrsq / self.rot_tol**2)
def __init__( self, symname, tgtaxis1, tgtaxis2, from_seg, *, tolerance=1.0, lever=50, to_seg=-1, space_group_str=None, cell_dist_scale=1.0, tgtaxis2_isects=[0, 1, 0], ): """ Worms criteria for non-intersecting axes re: unbounded things assume tgtaxis1 goes through origin tgtaxis2 intersects tgtaxis2_isects Args: symname (str): Symmetry identifier, to label stuff and look up the symdef file. tgtaxis1: Target axis 1. tgtaxis2: Target axis 2. from_seg (int): The segment # to start at. tolerance (float): A geometry/alignment error threshold. Vaguely Angstroms. lever (float): Tradeoff with distances and angles for a lever-like object. To convert an angle error to a distance error for an oblong shape. to_seg (int): The segment # to end at. space_group_str: The target space group. """ self.symname = symname self.cell_dist_scale = cell_dist_scale self.tgtaxis1 = np.asarray( tgtaxis1, dtype="f8" ) ## we are treating these as vectors for now, make it an array if it isn't yet, set array type to 8-type float self.tgtaxis2 = np.asarray(tgtaxis2, dtype="f8") # print(self.tgtaxis1.shape) # print(np.linalg.norm(tgtaxis1)) self.tgtaxis1 /= np.linalg.norm( self.tgtaxis1) # normalize target axes to 1,1,1 self.tgtaxis2 /= np.linalg.norm(self.tgtaxis2) if hm.angle(self.tgtaxis1, self.tgtaxis2) > np.pi / 2: self.tgtaxis2 = -self.tgtaxis2 self.from_seg = from_seg self.tolerance = tolerance self.lever = lever self.to_seg = to_seg self.space_group_str = space_group_str ## if you want to store arguments, you have to write these self.argument lines self.target_angle = np.arccos( np.abs(hm.hdot(self.tgtaxis1, self.tgtaxis2)) ) ## already set to a non- self.argument in this function # print(self.target_angle * (180 / np.pi)) self.is_cyclic = False self.origin_seg = None self.tgtaxis2_isects = tgtaxis2_isects
def score(self, segpos, verbosity=False, **kw): cen1 = segpos[self.from_seg][..., :, 3] cen2 = segpos[self.to_seg][..., :, 3] ax1 = segpos[self.from_seg][..., :, 2] ax2 = segpos[self.to_seg][..., :, 2] if self.nondistinct_axes: p, q = hm.line_line_closest_points_pa(cen1, ax1, cen2, ax2) dist = hm.hnorm(p - q) cen = (p + q) / 2 ax1c = hm.hnormalized(cen1 - cen) ax2c = hm.hnormalized(cen2 - cen) ax1 = np.where(hm.hdot(ax1, ax1c)[..., None] > 0, ax1, -ax1) ax2 = np.where(hm.hdot(ax2, ax2c)[..., None] > 0, ax2, -ax2) ang = np.arccos(hm.hdot(ax1, ax2)) else: dist = hm.line_line_distance_pa(cen1, ax1, cen2, ax2) ang = np.arccos(np.abs(hm.hdot(ax1, ax2))) roterr2 = (ang - self.tgtangle)**2 return np.sqrt(roterr2 / self.rot_tol**2 + (dist / self.tolerance)**2)
def primary_xform_commutator(units, state=1, **kw): print('stub 0:') print(units[0].stub) closest = None for j in range(1, 20): x = hm.hinv(units[0].stub) @ units[j].stub # print(f'stub {j}:') # print(units[j].stub) # print(x) axis, ang, cen = hm.axis_ang_cen_of(x) helical = hm.hdot(axis, x[:, 3]) print(j, helical, x[2, 3]) if abs(helical) > 0.01: return x return None
def alignment(self, segpos, **kwargs): if self.origin_seg is not None: return inv(segpos[self.origin_seg]) x_from = segpos[self.from_seg] x_to = segpos[self.to_seg] xhat = x_to @ inv(x_from) axis, ang, cen = hm.axis_ang_cen_of(xhat) # print('aln', axis) # print('aln', ang * 180 / np.pi) # print('aln', cen) # print('aln', xhat[..., :, 3]) dotz = hm.hdot(axis, Uz)[..., None] tgtaxis = np.where(dotz > 0, [0, 0, 1, 0], [0, 0, -1, 0]) align = hm.hrot((axis + tgtaxis) / 2, np.pi, cen) align[..., :3, 3] -= cen[..., :3] return align
def score(self, segpos, **kw): """ Score Args: segpos (lst): List of segment positions / coordinates. **kw I'll accept any "non-positional" argument as name = value, and store in a dictionary """ ## numpy arrays of how many things you are scoring, and a 4x4 translation/rotation matrix ax1 = segpos[self.from_seg][ ..., :, 2] ## from the first however many dimensions except the last two, give me the 2nd column, which for us is the Z-axis ax2 = segpos[self.to_seg][..., :, 2] #angle = hm.angle(ax1, ax2) ## homog angle function will compute the angle between two vectors, and give back an angle in radians angle = np.arccos( np.abs(hm.hdot(ax1, ax2)) ) ## this is better because it contains absolutel value, which ensures that you always get the smaller of the angles resulting from intersecting two lines return np.abs( (angle - self.target_angle) ) / self.tol * self.lever ## as tolerance goes up, you care about the angle error less. as lever goes up, you care about the angle error more.
def score(self, segpos, **kw): ax1 = segpos[self.from_seg][..., :, 2] ax2 = segpos[self.to_seg][..., :, 2] angle = np.arccos(np.abs(hm.hdot(ax1, ax2))) return np.abs( (angle - self.target_angle)) / self.tolerance * self.lever