def get_orient_angle(self, reference_point=numpy.array([0, 0, 0]), monomer_index=0, res_label='CA', radians=False): """ Angle between reference_point and self[monomer_index][res_label]. Notes ----- Angle is calculated using the dihedral angle, with the second and third points coming from the curve_primitive. Parameters ---------- reference_point : list, tuple or numpy.array of length 3. monomer_index : int Index of the Residue to centre. res_label : str Atom name for centred atom, e.g. "CA" or "OE1". radians : bool If True, then desired_angle is in radians instead of degrees. """ if (monomer_index < len(self)) and monomer_index != -1: adjacent_index = monomer_index + 1 elif (monomer_index == len(self)) or monomer_index == -1: adjacent_index = monomer_index - 1 else: raise ValueError( "centred_index ({0}) cannot be greater than the " "length of the polymer ({1})".format( monomer_index, len(self))) angle = dihedral(reference_point, self.curve_primitive[monomer_index]['CA'], self.curve_primitive[adjacent_index]['CA'], self[monomer_index][res_label]) if radians: angle = numpy.deg2rad(angle) return angle
def residues_per_turn(p): """ The number of residues per turn at each Monomer in the Polymer. Notes ----- Each element of the returned list is the number of residues per turn, at a point on the Polymer primitive. Calculated using the relative positions of the CA atoms and the primitive of the Polymer. Element i is the calculated from the dihedral angle using the CA atoms of the Monomers with indices [i, i+1] and the corresponding atoms of the primitive. The final value is None. Parameters ---------- p : ampal.Polypeptide `Polypeptide` from which residues per turn will be calculated. Returns ------- rpts : [float] Residue per turn values. """ cas = p.get_reference_coords() prim_cas = p.primitive.coordinates dhs = [ abs(dihedral(cas[i], prim_cas[i], prim_cas[i + 1], cas[i + 1])) for i in range(len(prim_cas) - 1) ] rpts = [360.0 / dh for dh in dhs] rpts.append(None) return rpts
def alpha_angles(p, reference_axis, tag=True, reference_axis_name='ref_axis'): """Alpha angle calculated using points on the primitive of helix and axis. Notes ----- The final value is None, since the angle calculation requires pairs of points along the primitive and axis. This is a generalisation of the calculation used to measure the tilt of a helix in a coiled-coil with respect to the central axis of the coiled coil. Parameters ---------- p : ampal.Polymer Reference `Polymer`. reference_axis : list(numpy.array or tuple or list) Length of reference_axis must equal length of the Polymer. Each element of reference_axis represents a point in R^3. tag : bool, optional If `True`, tags the Chain with the reference axis coordinates and each Residue with its alpha angle. Alpha angles are stored at the Residue level, but are calculated using the CA atom. reference_axis_name : str, optional Used to name the keys in tags at Chain and Residue level. Returns ------- alphas : list of float The alpha angle for the Polymer at each point of its primitive, in degrees. Raises ------ ValueError If the Polymer and the reference_axis have unequal length. """ if not len(p) == len(reference_axis): raise ValueError( "The reference axis must contain the same number of points " "as the Polymer primitive.") prim_cas = p.primitive.coordinates ref_points = reference_axis.coordinates alphas = [ abs( dihedral(ref_points[i + 1], ref_points[i], prim_cas[i], prim_cas[i + 1])) for i in range(len(prim_cas) - 1) ] alphas.append(None) if tag: p.tags[reference_axis_name] = reference_axis monomer_tag_name = 'alpha_angle_{0}'.format(reference_axis_name) for m, a in zip(p._monomers, alphas): m.tags[monomer_tag_name] = a return alphas
def build(self): """Builds the `HelicalHelix`.""" helical_helix = Polypeptide() primitive_coords = self.curve_primitive.coordinates helices = [Helix.from_start_and_end(start=primitive_coords[i], end=primitive_coords[i + 1], helix_type=self.minor_helix_type, aa=1) for i in range(len(primitive_coords) - 1)] residues_per_turn = self.minor_residues_per_turn( minor_repeat=self.minor_repeat) if residues_per_turn == 0: residues_per_turn = _helix_parameters[self.minor_helix_type][0] if self.minor_handedness == 'l': residues_per_turn *= -1 # initial phi_c_alpha value calculated using the first Helix in helices. if self.orientation != -1: initial_angle = dihedral(numpy.array([0, 0, 0]), primitive_coords[0], primitive_coords[1], helices[0][0]['CA']) else: initial_angle = dihedral( numpy.array([0, 0, primitive_coords[0][2]]), primitive_coords[0], numpy.array([primitive_coords[0][0], primitive_coords[0][1], primitive_coords[1][2]]), helices[0][0]['CA']) # angle required to achieve desired phi_c_alpha value of self.phi_c_alpha. addition_angle = self.phi_c_alpha - initial_angle for i, h in enumerate(helices): angle = (i * (360.0 / residues_per_turn)) + addition_angle h.rotate(angle=angle, axis=h.axis.unit_tangent, point=h.helix_start) helical_helix.extend(h) helical_helix.relabel_all() self._monomers = helical_helix._monomers[:] for monomer in self._monomers: monomer.ampal_parent = self return
def crick_angles(p, reference_axis, tag=True, reference_axis_name='ref_axis'): """Returns the Crick angle for each CA atom in the `Polymer`. Notes ----- The final value is in the returned list is `None`, since the angle calculation requires pairs of points on both the primitive and reference_axis. Parameters ---------- p : ampal.Polymer Reference `Polymer`. reference_axis : list(numpy.array or tuple or list) Length of reference_axis must equal length of the Polymer. Each element of reference_axis represents a point in R^3. tag : bool, optional If `True`, tags the `Polymer` with the reference axis coordinates and each Residue with its Crick angle. Crick angles are stored at the Residue level, but are calculated using the CA atom. reference_axis_name : str, optional Used to name the keys in tags at Chain and Residue level. Returns ------- cr_angles : list(float) The crick angles in degrees for each CA atom of the Polymer. Raises ------ ValueError If the Polymer and the reference_axis have unequal length. """ if not len(p) == len(reference_axis): raise ValueError( "The reference axis must contain the same number of points" " as the Polymer primitive.") prim_cas = p.primitive.coordinates p_cas = p.get_reference_coords() ref_points = reference_axis.coordinates cr_angles = [ dihedral(ref_points[i], prim_cas[i], prim_cas[i + 1], p_cas[i]) for i in range(len(prim_cas) - 1) ] cr_angles.append(None) if tag: p.tags[reference_axis_name] = reference_axis monomer_tag_name = 'crick_angle_{0}'.format(reference_axis_name) for m, c in zip(p._monomers, cr_angles): m.tags[monomer_tag_name] = c return cr_angles
def generate_complementary_strand(strand1): """Takes a SingleStrandHelix and creates the antisense strand.""" rise_adjust = ( strand1.rise_per_nucleotide * strand1.axis.unit_tangent) * 2 strand2 = NucleicAcidStrand.from_start_and_end( strand1.helix_end - rise_adjust, strand1.helix_start - rise_adjust, generate_antisense_sequence(strand1.base_sequence), phos_3_prime=strand1.phos_3_prime) ad_ang = dihedral(strand1[0]["C1'"]._vector, strand1.axis.start, strand2.axis.start + rise_adjust, strand2[-1]["C1'"]._vector) strand2.rotate( 225.0 + ad_ang, strand2.axis.unit_tangent, point=strand2.helix_start) # 225 is the base adjust return strand2
def measure_sidechain_torsion_angles(residue, verbose=True): """Calculates sidechain dihedral angles for a residue Parameters ---------- residue : [ampal.Residue] `Residue` object. verbose : bool, optional If `true`, tells you when a residue does not have any known dihedral angles to measure. Returns ------- chi_angles: [float] Length depends on residue type, in range [-pi, pi] [0] = chi1 [if applicable] [1] = chi2 [if applicable] [2] = chi3 [if applicable] [3] = chi4 [if applicable] """ chi_angles = [] aa = residue.mol_code if aa not in side_chain_dihedrals: if verbose: print("Amino acid {} has no known side-chain dihedral".format(aa)) else: for set_atoms in side_chain_dihedrals[aa]: required_for_dihedral = set_atoms[0:4] try: angle = dihedral(residue[required_for_dihedral[0]]._vector, residue[required_for_dihedral[1]]._vector, residue[required_for_dihedral[2]]._vector, residue[required_for_dihedral[3]]._vector) chi_angles.append(angle) except KeyError as k: print("{0} atom missing from residue {1} {2} " "- can't assign dihedral".format(k, residue.mol_code, residue.id)) chi_angles.append(None) return chi_angles
def measure_torsion_angles(residues): """Calculates the dihedral angles for a list of backbone atoms. Parameters ---------- residues : [ampal.Residue] List of `Residue` objects. Returns ------- torsion_angles : (float, float, float) One triple for each residue, containing torsion angles in the range [-pi, pi]. [0] omega [1] phi [2] psi For the first residue, omega and phi are not defined. For the final residue, psi is not defined. Raises ------ ValueError If the number of input residues is less than 2. """ if len(residues) < 2: torsion_angles = [(None, None, None)] * len(residues) else: torsion_angles = [] for i in range(len(residues)): if i == 0: res1 = residues[i] res2 = residues[i + 1] omega = None phi = None try: psi = dihedral(res1['N']._vector, res1['CA']._vector, res1['C']._vector, res2['N']._vector) except KeyError as k: print("{0} atom missing - can't assign psi".format(k)) psi = None torsion_angles.append((omega, phi, psi)) elif i == len(residues) - 1: res1 = residues[i - 1] res2 = residues[i] try: omega = dihedral(res1['CA']._vector, res1['C']._vector, res2['N']._vector, res2['CA']._vector) except KeyError as k: print("{0} atom missing - can't assign omega".format(k)) omega = None try: phi = dihedral(res1['C']._vector, res2['N']._vector, res2['CA']._vector, res2['C']._vector) except KeyError as k: print("{0} atom missing - can't assign phi".format(k)) phi = None psi = None torsion_angles.append((omega, phi, psi)) else: res1 = residues[i - 1] res2 = residues[i] res3 = residues[i + 1] try: omega = dihedral(res1['CA']._vector, res1['C']._vector, res2['N']._vector, res2['CA']._vector) except KeyError as k: print("{0} atom missing - can't assign omega".format(k)) omega = None try: phi = dihedral(res1['C']._vector, res2['N']._vector, res2['CA']._vector, res2['C']._vector) except KeyError as k: print("{0} atom missing - can't assign phi".format(k)) phi = None try: psi = dihedral(res2['N']._vector, res2['CA']._vector, res2['C']._vector, res3['N']._vector) except KeyError as k: print("{0} atom missing - can't assign psi".format(k)) psi = None torsion_angles.append((omega, phi, psi)) return torsion_angles
def build(self): """Build single DNA strand along z-axis, starting with P on x-axis""" ang_per_res = (2 * numpy.pi) / self.nucleotides_per_turn atom_offset_coords = _backbone_properties[self.helix_type]['atoms'] if self.handedness == 'l': handedness = -1 else: handedness = 1 base_atom_labels = _backbone_properties[self.helix_type]['labels'] monomers = [] mol_code_format = _backbone_properties[ self.helix_type]['mol_code_format'] for i, b in enumerate(self.base_sequence): nucleotide = Nucleotide(mol_code=mol_code_format.format(b), ampal_parent=self) atoms_dict = OrderedDict() if (i == (len(self.base_sequence) - 1)) and not self.phos_3_prime: # Do not include phosphate on last nucleotide atom_labels = base_atom_labels[3:] + [_bases[b]['labels'][2]] atom_offsets = { k: v for k, v in zip(atom_labels, atom_offset_coords[3:]) } else: atom_labels = base_atom_labels + [_bases[b]['labels'][2]] atom_offsets = { k: v for k, v in zip(atom_labels, atom_offset_coords) } for atom_label in atom_labels: r, zeta, z_shift = atom_offsets[atom_label] rot_ang = ((i * ang_per_res) + zeta) * handedness z = (self.rise_per_nucleotide * i) + z_shift coords = cylindrical_to_cartesian(radius=r, azimuth=rot_ang, z=z, radians=True) atom = Atom(coordinates=coords, element=atom_label[0], ampal_parent=nucleotide, res_label=atom_label) atoms_dict[atom_label] = atom base_ref = _bases[b]['ref_atom'] rot_adj = _bases[b]['rot_adj'] base_dict = OrderedDict( zip(_bases[b]['labels'], _bases[b]['atoms'])) translation, angle, axis, point = find_transformations( base_dict[base_ref], base_dict["C1'"], atoms_dict[base_ref]._vector, atoms_dict["C1'"]._vector) q1 = Quaternion.angle_and_axis(angle, axis) # Align N9 C1' for k, v in base_dict.items(): base_dict[k] = q1.rotate_vector(v, point) + translation # Rotate to align O4' axis = numpy.array(base_dict["C1'"]) - base_dict[base_ref] angle = dihedral(base_dict["O4'"], base_dict[base_ref], base_dict["C1'"], atoms_dict["O4'"]) - rot_adj q2 = Quaternion.angle_and_axis(angle, axis) for k, v in list(base_dict.items()): if k not in atoms_dict: atom = Atom(q2.rotate_vector(v, base_dict[base_ref]), element=k[0], ampal_parent=nucleotide, res_label=k) atoms_dict[k] = atom nucleotide.atoms = atoms_dict monomers.append(nucleotide) self._monomers = monomers self.relabel_monomers() self.relabel_atoms() return