def n_join(self, other, psi=-40.76, omega=-178.25, phi=-65.07,
               o_c_n_angle=None, c_n_ca_angle=None, c_n_length=None, relabel=True):
        """Joins other to self at the N-terminus via a peptide bond.

        Notes
        -----
        This function directly modifies self. It does not return a new object.

        Parameters
        ----------
        other: Residue or Polypeptide
        psi: float
            Psi torsion angle (degrees) between final `Residue` of other
            and first `Residue` of self.
        omega: float
            Omega torsion angle (degrees) between final `Residue` of
            other and first `Residue` of self.
        phi: float
            Phi torsion angle (degrees) between final `Residue` of other
            and first `Residue` of self.
        o_c_n_angle: float or None
            Desired angle between O, C (final `Residue` of other) and N
            (first `Residue` of self) atoms. If `None`, default value is
            taken from `ideal_backbone_bond_angles`.
        c_n_ca_angle: float or None
            Desired angle between C (final `Residue` of other) and N, CA
            (first `Residue` of self) atoms. If `None`, default value is taken
            from `ideal_backbone_bond_angles`.
        c_n_length: float or None
            Desired peptide bond length between final `Residue` of other
            and first `Residue` of self. If None, default value is taken
            from ideal_backbone_bond_lengths.
        relabel: bool
            If True, relabel_all is run on self before returning.

        Raises
        ------
        TypeError:
            If other is not a `Residue` or a `Polypeptide`
        """
        if isinstance(other, Residue):
            other = Polypeptide([other])
        if not isinstance(other, Polypeptide):
            raise TypeError(
                'Only Polypeptide or Residue objects can be joined to a Polypeptide')
        if abs(omega) >= 90:
            peptide_conformation = 'trans'
        else:
            peptide_conformation = 'cis'
        if o_c_n_angle is None:
            o_c_n_angle = ideal_backbone_bond_angles[peptide_conformation]['o_c_n']
        if c_n_ca_angle is None:
            c_n_ca_angle = ideal_backbone_bond_angles[peptide_conformation]['c_n_ca']
        if c_n_length is None:
            c_n_length = ideal_backbone_bond_lengths['c_n']
        r1 = self[0]
        r1_n = r1['N']._vector
        r1_ca = r1['CA']._vector
        r1_c = r1['C']._vector
        # p1 is point that will be used to position the C atom of r2.
        p1 = r1_ca[:]
        # rotate p1 by c_n_ca_angle, about axis perpendicular to the
        # r1_n, r1_ca, r1_c plane, passing through r1_ca.
        axis = numpy.cross((r1_ca - r1_n), (r1_c - r1_n))
        q = Quaternion.angle_and_axis(angle=c_n_ca_angle, axis=axis)
        p1 = q.rotate_vector(v=p1, point=r1_n)
        # Ensure p1 is separated from r1_n by the correct distance.
        p1 = r1_n + (c_n_length * unit_vector(p1 - r1_n))
        # translate other so that its final C atom is at p1
        other.translate(vector=(p1 - other[-1]['C']._vector))
        # Force CA-C=O-N to be in a plane, and fix O=C-N angle accordingly
        measured_dihedral = dihedral(
            other[-1]['CA'], other[-1]['C'], other[-1]['O'], r1['N'])
        desired_dihedral = 180.0
        axis = other[-1]['O'] - other[-1]['C']
        other.rotate(angle=(measured_dihedral - desired_dihedral),
                     axis=axis, point=other[-1]['C']._vector)
        axis = (numpy.cross(other[-1]['O'] - other[-1]
                            ['C'], r1['N'] - other[-1]['C']))
        measured_o_c_n = angle_between_vectors(
            other[-1]['O'] - other[-1]['C'], r1['N'] - other[-1]['C'])
        other.rotate(angle=(measured_o_c_n - o_c_n_angle),
                     axis=axis, point=other[-1]['C']._vector)
        # rotate other to obtain desired phi, omega, psi values at the join.
        measured_phi = dihedral(other[-1]['C'], r1['N'], r1['CA'], r1['C'])
        other.rotate(angle=(phi - measured_phi),
                     axis=(r1_n - r1_ca), point=r1_ca)
        measured_omega = dihedral(
            other[-1]['CA'], other[-1]['C'], r1['N'], r1['CA'])
        other.rotate(angle=(measured_omega - omega),
                     axis=(r1['N'] - other[-1]['C']), point=r1_n)
        measured_psi = dihedral(
            other[-1]['N'], other[-1]['CA'], other[-1]['C'], r1['N'])
        other.rotate(angle=-(measured_psi - psi), axis=(other[-1]['CA'] - other[-1]['C']),
                     point=other[-1]['CA']._vector)
        self._monomers = other._monomers + self._monomers
        if relabel:
            self.relabel_all()
        self.tags['assigned_ff'] = False
        return