def apply_coordinatesystem(A_xyz_ref, B_xyz_ref, A_xyz_frame, B_xyz_frame, com_A, com_B, _xvec=np.array([1, 0, 0]), _yvec=np.array([0, 1, 0]), _zvec=np.array([0, 0, 1])): """ Maps the reference frame vectors (coordinate system) onto the A and B domains of an immunoglobulin domain. Assumes that the reference is in the standard orientation (so transformed by standard_orientation): Then the global z-vector corresponds to the chains principal axis of inertia with corresponding lowest eigenvalue, and the x-vector is closest to the COM vector between the two domains while orthogonal to the z-vec. Parameters: ----- Required: A_xyz_ref -> Numpy array of shape (Nr of atoms, 3), contains xyz coordinates of all A chain atoms of the reference frame/structure B_xyz_ref -> Numpy array of shape (Nr of atoms, 3), contains xyz coordinates of all B chain atoms of the reference frame/structure A_xyz_frame -> Numpy array of shape (Nr of atoms, 3), contains xyz coordinates of all A chain atoms of the frame/structure from which angles will be calculated to the reference structure B_xyz_frame -> umpy array of shape (Nr of atoms, 3), contains xyz coordinates of all A chain atoms of the frame/structure from which angles will be calculated to the reference structure com_A -> Numpy array of shape (3), contains xyz coordinates of the A chain center of mass of the !reference structure! (Should be [0,0,0] in the standard orientation) com_B -> Numpy array of shape (3), contains xyz coordinates of the B chain center of mass of the !reference structure! (Should be [0,0,0] in the standard orientation) Optional: _xvec -> Numpy array of shape (1, 3). This vector is used as the third vector of the OCD coordinate system. Use the unit x vector if reference domains are in standard orientation. _yvec -> Numpy array of shape (1, 3). This vector is used as the second vector of the OCD coordinate system. Use the unit y vector if reference domains are in standard orientation. _zvec -> Numpy array of shape (1, 3). This vector is used as the first vector of the OCD coordinate system. Use the unit z vector if reference domains are in standard orientation. Returns: B_points -> List of 3 numpy arrays of shape (3), containing (in order) the transformed coordinates of the center of mass, the endpoint of the B1 vector and the endpoint of the B2 vector A_points -> List of 3 numpy arrays of shape (3), containing (in order) the transformed coordinates of the center of mass, the endpoint of the A1 vector and the endpoint of the A2 vector """ # Get transformation matrices by alligning the core of the domains: Reference is aligned onto Frame structure A_transform = compute_transformation(A_xyz_ref, A_xyz_frame) B_transform = compute_transformation(B_xyz_ref, B_xyz_frame) # Add vectors to center of mass to get vector endpoints in the reference domain -> these will then be transformed to the sample domains # The vectors are the standard z-vector and y-vector added to the center of mass - see above and in function standard_orientation why! B1 = com_B + _zvec B2 = com_B + _yvec B3 = com_B + _xvec A1 = com_A + _zvec A2 = com_A + _yvec A3 = com_A + _xvec # Apply the transfomation to the reference vectors B_points = np.array(B_transform.transform(np.array((com_B, B1, B2, B3)))) A_points = np.array(A_transform.transform(np.array((com_A, A1, A2, A3)))) return B_points, A_points
def test_transform2(): # For testing transform on all points when only subset for align xyz1_len = np.shape(xyz1)[0] T = alignment.compute_transformation(xyz4[0:xyz1_len, :], xyz1) xyz4_prime = T.transform(xyz4) eq(xyz1, xyz4_prime[0:xyz1_len, :])
def test_transform2(): # For testing transform on all points when only subset for align xyz1_len = np.shape(xyz1)[0] T = alignment.compute_transformation(xyz4[0:xyz1_len,:], xyz1) xyz4_prime = T.transform(xyz4) eq(xyz1, xyz4_prime[0:xyz1_len,:])
def standard_orientation(stripped_domains, mask_A, mask_B, out): ''' Calculates standard orientation for two domains given as masks A and B applied to trajectory traj. Standard orientations are calculated as the orientation where the lowest moment of inertia eigenvector of the domain is aligned with z, the x axis is as close to the vector bewtween the centers of mass of the two domains as possible. The y vector is calculated as the negative crossproduct of the z-vector (lowest eigenvector) and x-vector(projection of com vector), giving a right handed coordinate system. The whole domain is alligned to this coordinate system in such a way that the center of mass is in the origin. The coordinates of the two alligned domains are returned. The full reference structure is used for the calculation of the coordinate system, but the coordinates are stripped before output according to masks A/B -> coordinate system only dependant on refstructure, not on the necessary mask for further alignment This is necessary, because crystal structure files might contain fewer residues than the reference structure which would mess up the alignment Parameters ---- Requires: stripped_domains, a list containing two pytraj trajectories corresponding to domains A (entry 0) and B (entry 1) mask_A, a str giving the cpptraj/pytraj mask corresponding to domain A in the trajectory - note that this mask mask_B, a str giving the cpptraj/pytraj mask corresponding to domain B in the trajectory Returns: transformed_coordinates_A; a numpy array of shape (n_atoms, 3), giving the reoriented coordinates of domain A to be used as reference orientation for ABangle calculations transformed_coordinates_B; a numpy array of shape (n_atoms, 3), giving the reoriented coordinates of domain B to be used as reference orientation for ABangle calculations ''' ### Apply both masks and calculate masses, com, coordinates and a reference point coms = [ pt.center_of_mass(stripped_domains[0]), pt.center_of_mass(stripped_domains[1]) ] x_vec = [1, 0, 0] y_vec = [0, 1, 0] z_vec = [0, 0, 1] origin = [0, 0, 0] masks = [mask_A, mask_B] unit_vectors = np.array([x_vec, y_vec, z_vec, origin]) #Orderd xyz results = [] for i, domain in enumerate(stripped_domains): partner = 0 if i == 1 else 1 #selects index of partnered domain mask = masks[i] ### Strip down coordinates to mask, does not affect calculation of coordinate system / moment of inertia tensor, just the reference coordinate data to be returned ### This is used in case the reference structure has more residues in it than the sample structure (see function new_maks) try: if mask != None: traj_stripped = domain[:].strip('!(' + mask + ')') else: traj_stripped = domain except: print( 'WARNING: Reference structure could not be further stripped. This generally happens if the trajectory contains more residues than the reference structure. ' ) traj_stripped = domain traj_partner = stripped_domains[partner] masses = domain.top.mass com_traj = coms[i] com_partner = coms[partner] ref_coordinates = domain.xyz ref_point = domain.xyz[0, 0] inertia_tensor = compute_inertia_tensor( ref_coordinates - np.expand_dims(com_traj, axis=1), masses) evals, evecs = np.linalg.eigh(inertia_tensor[0]) ### Reorder evecs to conform to xyz orientation. Largest evec should be x, lowest z evals = evals[::-1] evecs = evecs.T[::-1] evecs = orient_evecs(evecs, com_traj[0], ref_point, com_partner[0]) evec_orientation = np.append( evecs + com_traj[0], [com_traj[0]], axis=0 ) #add center of mass to evec array to conform to xyz+origin ordering, same as unit_vectors ### Apply first transformation: Aligns eigenvectors of inertia tensor with xyz unit vectors. The center of mass is now in the origin transformation = compute_transformation(evec_orientation, unit_vectors) z_aligned_ref_coords = transformation.transform(traj_stripped.xyz[0]) ### Calculate vectors for second transformation: new_com_partner is the transformed partner com, which is then projected onto the xy plane and stored as a unitvector. new_com_vector = transformation.transform(com_partner[0]) new_com_vector[2] = 0 #projection on xy plane new_com_vector = unit_vec(new_com_vector) orthogonal_vector = -np.cross(new_com_vector, z_vec) ### Check angle of z-vec to com vector to make sure the coordinate system makes sense, they should be roughly orthogonal if abs(np.dot(z_vec, new_com_vector)) > 0.25: print( 'WARNING: Ange between center axis and Vectors in domain {} is lower than $\pm$ 75 degrees. Please consider checking visualizations of the coordinate system to ensure that this works for your purpose.' ) ### Do second transformation. Lowest eigenvector is still z, x is now the projection of the com vector -> closest possible alignment of x and com while keeping x orthogonal to z reference_orientation = np.array([ unit_vec(new_com_vector), unit_vec(orthogonal_vector), z_vec, origin ]) second_transformation = compute_transformation(reference_orientation, unit_vectors) x_z_aligned_ref_coords = second_transformation.transform( z_aligned_ref_coords) results.append(x_z_aligned_ref_coords) transformed_coordinates_A = results[0] transformed_coordinates_B = results[1] return transformed_coordinates_A, transformed_coordinates_B #Reference coordinates A and B
def test_transform(): T = alignment.compute_transformation(xyz2, xyz1) xyz2_prime = T.transform(xyz2) eq(xyz1, xyz2_prime)
def test_transform(): T = alignment.compute_transformation(xyz2, xyz1) xyz2_prime = T.transform(xyz2) eq(xyz1, xyz2_prime)