<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Calculating the persistence length of polymers using MDAnalysis
In analysing polymers, the persistence length is a measure of a chains stiffness.  The persistence length is the distance at which the direction of two points on a polymer chain becomes decorrelated.  High persistence lengths indicate that the polymer chain is rigid and doesn't change direction, low persistence lengths indicate that the polymer chain has little memory of its orientation.
The bond autocorrelation function $C(n)$ measures the average cosine of the angle between bond vector $\mathbf{a_i}$ and a bond vector $n$ bonds away. 
$$C(n) = \langle \cos\theta_{i, i+n} \rangle= \langle \mathbf{a_i} \cdot \mathbf{a_{i+n}} \rangle$$
This is then fitted to an exponential decay, where $l_B$ is the average bond length, and $l_P$ is the persistence length.
$$C(n) \approx \exp\left(-\frac{n l_B}{l_P}\right)$$
To get started, we'll need to import MDAnalysis and the PersistenceLength tool.  This tutorial was made to work on version 0.18.0 onwards of MDAnalysis.
Step1:  Next we'll load up a Universe.
This example is working with a single frame of a polyamide system.
In this system, each chain is a unique residue, this is defined within our topology file.
Step2:  We'll need to create an AtomGroup (an array of atoms) for each polymer chain.
As our polymer chain is linear, we can select the backbone atoms based on the atom type.
Step3:  It is important that the contents of each AtomGroup are in order.  Selections done using select_atoms will always be sorted.
This can be checked by listing the AtomGroup.
Step4:  This list of AtomGroups is the required input for the PersistenceLength analysis class.
After creating the instance, we call the run method on it to perform the calculation.  In this case, the Universe only has a single frame to analyse, however the tool would normally iterate over all available frames to gather better statistics.
Step5:  This has created the .results attribute on the analysis class.
Plotting this can let us see $C(n)$.  The function becomes noiser at high values (where the statistics are poorer), but is quite smooth for lower values.  This is because there are many ways to measure $C(1)$, but few ways to measure $C(100)$.
Step6:  The tool can then perform the exponential decay fit for us, which populate the .lp attribute.
Step7:  Finally to check the validity of the fit, we can plot the fit against the results.
<ASSISTANT_TASK:>
Python Code:
from __future__ import print_function
import MDAnalysis as mda
from MDAnalysis.analysis.polymer import PersistenceLength
import matplotlib.pyplot as plt
%matplotlib inline
Explanation: Calculating the persistence length of polymers using MDAnalysis
In analysing polymers, the persistence length is a measure of a chains stiffness.  The persistence length is the distance at which the direction of two points on a polymer chain becomes decorrelated.  High persistence lengths indicate that the polymer chain is rigid and doesn't change direction, low persistence lengths indicate that the polymer chain has little memory of its orientation.
The bond autocorrelation function $C(n)$ measures the average cosine of the angle between bond vector $\mathbf{a_i}$ and a bond vector $n$ bonds away. 
$$C(n) = \langle \cos\theta_{i, i+n} \rangle= \langle \mathbf{a_i} \cdot \mathbf{a_{i+n}} \rangle$$
This is then fitted to an exponential decay, where $l_B$ is the average bond length, and $l_P$ is the persistence length.
$$C(n) \approx \exp\left(-\frac{n l_B}{l_P}\right)$$
To get started, we'll need to import MDAnalysis and the PersistenceLength tool.  This tutorial was made to work on version 0.18.0 onwards of MDAnalysis.
End of explanation
u = mda.Universe('plength.gro')
print('We have a universe: {}'.format(u))
print('We have {} chains'.format(len(u.residues)))
print('Our atom types are: {}'.format(set(u.atoms.types)))
Explanation: Next we'll load up a Universe.
This example is working with a single frame of a polyamide system.
In this system, each chain is a unique residue, this is defined within our topology file.
End of explanation
ags = [r.atoms.select_atoms('type C or type N') for r in u.residues]
list(ags)
Explanation: We'll need to create an AtomGroup (an array of atoms) for each polymer chain.
As our polymer chain is linear, we can select the backbone atoms based on the atom type.
End of explanation
list(ags[0][:10])
Explanation: It is important that the contents of each AtomGroup are in order.  Selections done using select_atoms will always be sorted.
This can be checked by listing the AtomGroup.
End of explanation
p = PersistenceLength(ags)
p.run()
Explanation: This list of AtomGroups is the required input for the PersistenceLength analysis class.
After creating the instance, we call the run method on it to perform the calculation.  In this case, the Universe only has a single frame to analyse, however the tool would normally iterate over all available frames to gather better statistics.
End of explanation
plt.ylabel('C(n)')
plt.xlabel('n')
plt.plot(p.results)
Explanation: This has created the .results attribute on the analysis class.
Plotting this can let us see $C(n)$.  The function becomes noiser at high values (where the statistics are poorer), but is quite smooth for lower values.  This is because there are many ways to measure $C(1)$, but few ways to measure $C(100)$.
End of explanation
p.perform_fit()
print("The persistence length is {:.4f} A".format(p.lp))
Explanation: The tool can then perform the exponential decay fit for us, which populate the .lp attribute.
End of explanation
p.plot()
Explanation: Finally to check the validity of the fit, we can plot the fit against the results.
End of explanation
<END_TASK>