docs: 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