<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://chemwiki.ch.ic.ac.uk/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Fgk17</id>
	<title>ChemWiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://chemwiki.ch.ic.ac.uk/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Fgk17"/>
	<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/wiki/Special:Contributions/Fgk17"/>
	<updated>2026-04-04T01:18:47Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.0</generator>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796674</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796674"/>
		<updated>2019-11-20T11:54:48Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 12 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 1}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\sum^N_i\sum_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt; is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 3}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 4}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; \mbox{equation 5}&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; \mbox{equation 6}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|250px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|250px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|250px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
From these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using a modified version of the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin. The code for modified version of the ILtemperature.py file is below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from matplotlib import pylab as pl&lt;br /&gt;
import numpy as np&lt;br /&gt;
&lt;br /&gt;
n_rows = 2 #number of rows in the lattice&lt;br /&gt;
n_cols = 2 #number of collums&lt;br /&gt;
il = IsingLattice(n_rows, n_cols)&lt;br /&gt;
il.lattice = np.ones((n_rows, n_cols))&lt;br /&gt;
spins = n_rows*n_cols #number of lattice sites&lt;br /&gt;
runtime = 100000&lt;br /&gt;
times = range(runtime) #number of steps run&lt;br /&gt;
temps = np.arange(0.25, 5, 0.05) #range of temperatures and interval size&lt;br /&gt;
energies = []&lt;br /&gt;
magnetisations = []&lt;br /&gt;
energysq = []&lt;br /&gt;
magnetisationsq = []&lt;br /&gt;
Eerr=[]&lt;br /&gt;
Merr=[]&lt;br /&gt;
&lt;br /&gt;
for t in temps: #calles every number in the list of temperatures&lt;br /&gt;
    for i in times: #runs through each step in the cycle &lt;br /&gt;
        &lt;br /&gt;
        energy, magnetisation = il.montecarlostep(t) #outputs the energy after each step&lt;br /&gt;
    aveE, aveE2, aveM, aveM2, n_cycles = il.statistics() #collects the energy at the end of each run of an individual temperature &lt;br /&gt;
    energies.append(aveE) #compiles a list of energies&lt;br /&gt;
    energysq.append(aveE2) #compiles a list of energies squared&lt;br /&gt;
    magnetisations.append(aveM) #compiles a list of magnetisations&lt;br /&gt;
    magnetisationsq.append(aveM2) #compiles a list of magnetisations squared &lt;br /&gt;
    Eerr.append(abs(aveE**2-aveE2)**0.5) # calculates erro in the energies&lt;br /&gt;
    Merr.append(abs(aveM**2-aveM2)**0.5) #calcualates error in the magnetisation &lt;br /&gt;
    &lt;br /&gt;
    #reset the IL object for the next cycle&lt;br /&gt;
    il.E = 0.0&lt;br /&gt;
    il.E2 = 0.0&lt;br /&gt;
    il.M = 0.0&lt;br /&gt;
    il.M2 = 0.0&lt;br /&gt;
    il.n_cycles = 0&lt;br /&gt;
    &lt;br /&gt;
  #creates 2 plots one for energy and on for magnetisation   &lt;br /&gt;
fig = pl.figure()&lt;br /&gt;
enerax = fig.add_subplot(2,1,1)&lt;br /&gt;
enerax.set_ylabel(&amp;quot;Energy per spin&amp;quot;)&lt;br /&gt;
enerax.set_xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
magax = fig.add_subplot(2,1,2)&lt;br /&gt;
magax.set_ylabel(&amp;quot;Magnetisation per spin&amp;quot;)&lt;br /&gt;
magax.set_xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
enerax.plot(temps, np.array(energies)/spins, color=&#039;black&#039;) #plots average energy per spin vs temp &lt;br /&gt;
enerax.errorbar(temps, np.array(energies)/spins,yerr=np.array(Eerr)/spins, color=&#039;pink&#039;,linestyle=&#039;&#039;) #plots error in the energy per spin vs temp&lt;br /&gt;
&lt;br /&gt;
magax.plot(temps, np.array(magnetisations)/spins,color=&#039;black&#039;)#plots average magnetisation per spin vs temp &lt;br /&gt;
magax.errorbar(temps, np.array(magnetisations)/spins,yerr=np.array(Merr)/spins,color=&#039;pink&#039;,linestyle=&#039;&#039;)#plots error in the mgnetisation per spin vs temp&lt;br /&gt;
pl.show()&lt;br /&gt;
#saves a data file containing temperature, energy, energy squared, magnetisatio, magnetisation squared&lt;br /&gt;
final_data = np.column_stack((temps, energies, energysq, magnetisations, magnetisationsq))&lt;br /&gt;
np.savetxt(&amp;quot;4x4.dat&amp;quot;, final_data)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. Also as the temperature increases the average magnetisation per spin changes and goes from 1 down to 0 due to the en tropic driving forces. According the equation 7 the gradient of the average energy per spin vs temperature is the heat capacity.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
&lt;br /&gt;
[[File:FkEn vs T.png|200px|thumb|figure 6,a:how energy per spin changes with temperature and lattice size]] &lt;br /&gt;
[[File:FkMag vs T.png|200px|thumb|figure 6,b:how magnetisation per spin changes with temperature and lattice size]] &lt;br /&gt;
these graphs are plotted using the following code&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
x32=np.loadtxt(&#039;32x32.dat&#039;) #extracts the data from the data file&lt;br /&gt;
temp32=x32[:,0] #extracts the temperatures as a list from the data&lt;br /&gt;
E32=x32[:,1] #extracts the average energies from the data  &lt;br /&gt;
EE32=x32[:,2] #extracts the average square of the energies from the data &lt;br /&gt;
M32=x32[:,3] #extracts the average magnetisation from the data&lt;br /&gt;
MM32=x32[:,4] #extracts the average square of the magnetisations from the data &lt;br /&gt;
varE32=(np.array(EE32)-np.array(E32)**2)/(32*32) #calculates the variance of the energies per spin&lt;br /&gt;
varM32=(np.array(MM32)-np.array(M32)**2)/(32*32) #calculates the variance of the magnetisation per spin&lt;br /&gt;
Cv32=heat_capacity(temp32,varE32) #calculates the heat capacities&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,M32/(32*32),label=&#039;32x32&#039;) #plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;magnetisation per spin&#039;)&lt;br /&gt;
pl.title(&#039;how magnetisation per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;mag_vs_T.png&#039;) &lt;br /&gt;
pl.show()&lt;br /&gt;
&lt;br /&gt;
#plots the energy per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,E32/(32*32),label=&#039;32x32&#039;)&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;energy per spin&#039;)&lt;br /&gt;
pl.title(&#039;how energy per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;en_vs_T.png&#039;)&lt;br /&gt;
pl.show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As can be seen in figure 6a, the difference in the curve energy per spin vs temperature for 16x16 and 32x32 is small suggesting that they are both able to model the long range fluctuations. However there is still a slight in the magnetisation per spin vs temperature curve as seen in figure 6b, meaning that possibly the 16x16 lattice is not modelling them particularly well and a 32x32 lattice models them more accurately. If the lattice is to small the effect of one spin flipping is felt more greatly due to the periodic nature of the lattice if this is to small these spins can exert and effect on another. Both of these effects mean that the larger the lattice which is used the batter the prediction as it will model the long range fluctuation better and also eliminate the effect of the spin on its self as this decreases rapidly with distance.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}\quad \mbox{equation 7}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\mbox{show that} \quad C_v = \frac{Var[E]}{k_BT^2} \quad \mbox{equation 8}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
Figures 8,9 and 10 how how the data generated by our selves compares to the C++ data.&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|300px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
[[File:FkLvsTc.png|300px|thumb|figure 13: &amp;lt;math&amp;gt;\frac{1}{lattice length}\quad \mbox{vs} \quad T_{c,L} &amp;lt;/math&amp;gt;]]&lt;br /&gt;
As the Curie temperature changes with the size of the lattice according to equation 7.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{c,L}=\frac{A}{L}+T_{c,\infty}\quad \mbox{equation 8}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
where L is the lattice side length A is a constant &amp;lt;math&amp;gt;T_{c,L}&amp;lt;/math&amp;gt; is the Curie temperature for a given lattice size and &amp;lt;math&amp;gt;T_{c,\infty}&amp;lt;/math&amp;gt; is the Curie temperature at infinity.&lt;br /&gt;
&lt;br /&gt;
The curie temperature of an infinite lattice can be predicted by finding the y intercept of the graph in figure 13. The Curie temperature of an infinite lattice is predicted to be 2.27681069 through the use of the graph. that predicted in literature is 2.16667 to the second order&amp;lt;ref&amp;gt;P. Li, Y. Song, Phys. Lett. A, 2001, &#039;&#039;&#039;289&#039;&#039;&#039;, 147-150.&amp;lt;/ref&amp;gt;. The value predicted by our model gives relatively good agreement. The major sources of error are likely to be due to the size of the lattices being looked at are small meaning that a by only flipping one spin there is a large effect on the average energy per spin. Also a grater number of montecarlo steps where used this would enable a better estimate of the energy to be calculated as the energy will be closer to the average energy for more of the steps.&lt;br /&gt;
&lt;br /&gt;
==final Isinglattic code==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;creating the Ising lattice model&#039;&#039;&#039;&lt;br /&gt;
import numpy as np&lt;br /&gt;
&lt;br /&gt;
class IsingLattice:&lt;br /&gt;
# create statting values so as to keep runing totals of E, E^2, M, M^2 and n_cycles&lt;br /&gt;
    E = 0.0&lt;br /&gt;
    E2 = 0.0&lt;br /&gt;
    M = 0.0&lt;br /&gt;
    M2 = 0.0&lt;br /&gt;
&lt;br /&gt;
    n_cycles = 1&lt;br /&gt;
&lt;br /&gt;
    def __init__(self, n_rows, n_cols):&lt;br /&gt;
        self.n_rows = n_rows #number of rows in the lattise&lt;br /&gt;
        self.n_cols = n_cols #number of colums in the lattise&lt;br /&gt;
        self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols)) #generate random spins for each lattice site&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
        &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
    &lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        #returnes energy and magentisation of resulting state&lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
&lt;br /&gt;
    def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        #rerurnes E,E^2,M,M^2 and step number&lt;br /&gt;
        return E, E2, M, M2, n&lt;br /&gt;
        &lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Refrences==&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796673</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796673"/>
		<updated>2019-11-20T11:54:21Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 12 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 1}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\sum^N_i\sum_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt; is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 3}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 4}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; \mbox{equation 5}&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; \mbox{equation 6}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|250px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|250px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|250px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
From these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using a modified version of the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin. The code for modified version of the ILtemperature.py file is below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from matplotlib import pylab as pl&lt;br /&gt;
import numpy as np&lt;br /&gt;
&lt;br /&gt;
n_rows = 2 #number of rows in the lattice&lt;br /&gt;
n_cols = 2 #number of collums&lt;br /&gt;
il = IsingLattice(n_rows, n_cols)&lt;br /&gt;
il.lattice = np.ones((n_rows, n_cols))&lt;br /&gt;
spins = n_rows*n_cols #number of lattice sites&lt;br /&gt;
runtime = 100000&lt;br /&gt;
times = range(runtime) #number of steps run&lt;br /&gt;
temps = np.arange(0.25, 5, 0.05) #range of temperatures and interval size&lt;br /&gt;
energies = []&lt;br /&gt;
magnetisations = []&lt;br /&gt;
energysq = []&lt;br /&gt;
magnetisationsq = []&lt;br /&gt;
Eerr=[]&lt;br /&gt;
Merr=[]&lt;br /&gt;
&lt;br /&gt;
for t in temps: #calles every number in the list of temperatures&lt;br /&gt;
    for i in times: #runs through each step in the cycle &lt;br /&gt;
        &lt;br /&gt;
        energy, magnetisation = il.montecarlostep(t) #outputs the energy after each step&lt;br /&gt;
    aveE, aveE2, aveM, aveM2, n_cycles = il.statistics() #collects the energy at the end of each run of an individual temperature &lt;br /&gt;
    energies.append(aveE) #compiles a list of energies&lt;br /&gt;
    energysq.append(aveE2) #compiles a list of energies squared&lt;br /&gt;
    magnetisations.append(aveM) #compiles a list of magnetisations&lt;br /&gt;
    magnetisationsq.append(aveM2) #compiles a list of magnetisations squared &lt;br /&gt;
    Eerr.append(abs(aveE**2-aveE2)**0.5) # calculates erro in the energies&lt;br /&gt;
    Merr.append(abs(aveM**2-aveM2)**0.5) #calcualates error in the magnetisation &lt;br /&gt;
    &lt;br /&gt;
    #reset the IL object for the next cycle&lt;br /&gt;
    il.E = 0.0&lt;br /&gt;
    il.E2 = 0.0&lt;br /&gt;
    il.M = 0.0&lt;br /&gt;
    il.M2 = 0.0&lt;br /&gt;
    il.n_cycles = 0&lt;br /&gt;
    &lt;br /&gt;
  #creates 2 plots one for energy and on for magnetisation   &lt;br /&gt;
fig = pl.figure()&lt;br /&gt;
enerax = fig.add_subplot(2,1,1)&lt;br /&gt;
enerax.set_ylabel(&amp;quot;Energy per spin&amp;quot;)&lt;br /&gt;
enerax.set_xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
magax = fig.add_subplot(2,1,2)&lt;br /&gt;
magax.set_ylabel(&amp;quot;Magnetisation per spin&amp;quot;)&lt;br /&gt;
magax.set_xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
enerax.plot(temps, np.array(energies)/spins, color=&#039;black&#039;) #plots average energy per spin vs temp &lt;br /&gt;
enerax.errorbar(temps, np.array(energies)/spins,yerr=np.array(Eerr)/spins, color=&#039;pink&#039;,linestyle=&#039;&#039;) #plots error in the energy per spin vs temp&lt;br /&gt;
&lt;br /&gt;
magax.plot(temps, np.array(magnetisations)/spins,color=&#039;black&#039;)#plots average magnetisation per spin vs temp &lt;br /&gt;
magax.errorbar(temps, np.array(magnetisations)/spins,yerr=np.array(Merr)/spins,color=&#039;pink&#039;,linestyle=&#039;&#039;)#plots error in the mgnetisation per spin vs temp&lt;br /&gt;
pl.show()&lt;br /&gt;
#saves a data file containing temperature, energy, energy squared, magnetisatio, magnetisation squared&lt;br /&gt;
final_data = np.column_stack((temps, energies, energysq, magnetisations, magnetisationsq))&lt;br /&gt;
np.savetxt(&amp;quot;4x4.dat&amp;quot;, final_data)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;\pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. Also as the temperature increases the average magnetisation per spin changes and goes from 1 down to 0 due to the en tropic driving forces. According the equation 7 the gradient of the average energy per spin vs temperature is the heat capacity.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
&lt;br /&gt;
[[File:FkEn vs T.png|200px|thumb|figure 6,a:how energy per spin changes with temperature and lattice size]] &lt;br /&gt;
[[File:FkMag vs T.png|200px|thumb|figure 6,b:how magnetisation per spin changes with temperature and lattice size]] &lt;br /&gt;
these graphs are plotted using the following code&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
x32=np.loadtxt(&#039;32x32.dat&#039;) #extracts the data from the data file&lt;br /&gt;
temp32=x32[:,0] #extracts the temperatures as a list from the data&lt;br /&gt;
E32=x32[:,1] #extracts the average energies from the data  &lt;br /&gt;
EE32=x32[:,2] #extracts the average square of the energies from the data &lt;br /&gt;
M32=x32[:,3] #extracts the average magnetisation from the data&lt;br /&gt;
MM32=x32[:,4] #extracts the average square of the magnetisations from the data &lt;br /&gt;
varE32=(np.array(EE32)-np.array(E32)**2)/(32*32) #calculates the variance of the energies per spin&lt;br /&gt;
varM32=(np.array(MM32)-np.array(M32)**2)/(32*32) #calculates the variance of the magnetisation per spin&lt;br /&gt;
Cv32=heat_capacity(temp32,varE32) #calculates the heat capacities&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,M32/(32*32),label=&#039;32x32&#039;) #plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;magnetisation per spin&#039;)&lt;br /&gt;
pl.title(&#039;how magnetisation per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;mag_vs_T.png&#039;) &lt;br /&gt;
pl.show()&lt;br /&gt;
&lt;br /&gt;
#plots the energy per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,E32/(32*32),label=&#039;32x32&#039;)&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;energy per spin&#039;)&lt;br /&gt;
pl.title(&#039;how energy per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;en_vs_T.png&#039;)&lt;br /&gt;
pl.show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As can be seen in figure 6a, the difference in the curve energy per spin vs temperature for 16x16 and 32x32 is small suggesting that they are both able to model the long range fluctuations. However there is still a slight in the magnetisation per spin vs temperature curve as seen in figure 6b, meaning that possibly the 16x16 lattice is not modelling them particularly well and a 32x32 lattice models them more accurately. If the lattice is to small the effect of one spin flipping is felt more greatly due to the periodic nature of the lattice if this is to small these spins can exert and effect on another. Both of these effects mean that the larger the lattice which is used the batter the prediction as it will model the long range fluctuation better and also eliminate the effect of the spin on its self as this decreases rapidly with distance.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}\quad \mbox{equation 7}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\mbox{show that} \quad C_v = \frac{Var[E]}{k_BT^2} \quad \mbox{equation 8}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
Figures 8,9 and 10 how how the data generated by our selves compares to the C++ data.&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|300px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
[[File:FkLvsTc.png|300px|thumb|figure 13: &amp;lt;math&amp;gt;\frac{1}{lattice length}\quad \mbox{vs} \quad T_{c,L} &amp;lt;/math&amp;gt;]]&lt;br /&gt;
As the Curie temperature changes with the size of the lattice according to equation 7.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{c,L}=\frac{A}{L}+T_{c,\infty}\quad \mbox{equation 8}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
where L is the lattice side length A is a constant &amp;lt;math&amp;gt;T_{c,L}&amp;lt;/math&amp;gt; is the Curie temperature for a given lattice size and &amp;lt;math&amp;gt;T_{c,\infty}&amp;lt;/math&amp;gt; is the Curie temperature at infinity.&lt;br /&gt;
&lt;br /&gt;
The curie temperature of an infinite lattice can be predicted by finding the y intercept of the graph in figure 13. The Curie temperature of an infinite lattice is predicted to be 2.27681069 through the use of the graph. that predicted in literature is 2.16667 to the second order&amp;lt;ref&amp;gt;P. Li, Y. Song, Phys. Lett. A, 2001, &#039;&#039;&#039;289&#039;&#039;&#039;, 147-150.&amp;lt;/ref&amp;gt;. The value predicted by our model gives relatively good agreement. The major sources of error are likely to be due to the size of the lattices being looked at are small meaning that a by only flipping one spin there is a large effect on the average energy per spin. Also a grater number of montecarlo steps where used this would enable a better estimate of the energy to be calculated as the energy will be closer to the average energy for more of the steps.&lt;br /&gt;
&lt;br /&gt;
==final Isinglattic code==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;creating the Ising lattice model&#039;&#039;&#039;&lt;br /&gt;
import numpy as np&lt;br /&gt;
&lt;br /&gt;
class IsingLattice:&lt;br /&gt;
# create statting values so as to keep runing totals of E, E^2, M, M^2 and n_cycles&lt;br /&gt;
    E = 0.0&lt;br /&gt;
    E2 = 0.0&lt;br /&gt;
    M = 0.0&lt;br /&gt;
    M2 = 0.0&lt;br /&gt;
&lt;br /&gt;
    n_cycles = 1&lt;br /&gt;
&lt;br /&gt;
    def __init__(self, n_rows, n_cols):&lt;br /&gt;
        self.n_rows = n_rows #number of rows in the lattise&lt;br /&gt;
        self.n_cols = n_cols #number of colums in the lattise&lt;br /&gt;
        self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols)) #generate random spins for each lattice site&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
        &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
    &lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        #returnes energy and magentisation of resulting state&lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
&lt;br /&gt;
    def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        #rerurnes E,E^2,M,M^2 and step number&lt;br /&gt;
        return E, E2, M, M2, n&lt;br /&gt;
        &lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Refrences==&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796651</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796651"/>
		<updated>2019-11-20T11:35:48Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 19 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 1}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\sum^N_i\sum_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt; is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 3}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 4}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; \mbox{equation 5}&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; \mbox{equation 6}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|250px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|250px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|250px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
From these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. Also as the temperature increases the average magnetisation per spin changes and goes from 1 down to 0 due to the en tropic driving forces. According the equation 7 the gradient of the average energy per spin vs temperature is the heat capacity.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
&lt;br /&gt;
[[File:FkEn vs T.png|200px|thumb|figure 6,a:how energy per spin changes with temperature and lattice size]] &lt;br /&gt;
[[File:FkMag vs T.png|200px|thumb|figure 6,b:how magnetisation per spin changes with temperature and lattice size]] &lt;br /&gt;
these graphs are plotted using the following code&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
x32=np.loadtxt(&#039;32x32.dat&#039;) #extracts the data from the data file&lt;br /&gt;
temp32=x32[:,0] #extracts the temperatures as a list from the data&lt;br /&gt;
E32=x32[:,1] #extracts the average energies from the data  &lt;br /&gt;
EE32=x32[:,2] #extracts the average square of the energies from the data &lt;br /&gt;
M32=x32[:,3] #extracts the average magnetisation from the data&lt;br /&gt;
MM32=x32[:,4] #extracts the average square of the magnetisations from the data &lt;br /&gt;
varE32=(np.array(EE32)-np.array(E32)**2)/(32*32) #calculates the variance of the energies per spin&lt;br /&gt;
varM32=(np.array(MM32)-np.array(M32)**2)/(32*32) #calculates the variance of the magnetisation per spin&lt;br /&gt;
Cv32=heat_capacity(temp32,varE32) #calculates the heat capacities&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,M32/(32*32),label=&#039;32x32&#039;) #plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;magnetisation per spin&#039;)&lt;br /&gt;
pl.title(&#039;how magnetisation per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;mag_vs_T.png&#039;) &lt;br /&gt;
pl.show()&lt;br /&gt;
&lt;br /&gt;
#plots the energy per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,E32/(32*32),label=&#039;32x32&#039;)&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;energy per spin&#039;)&lt;br /&gt;
pl.title(&#039;how energy per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;en_vs_T.png&#039;)&lt;br /&gt;
pl.show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As can be seen in figure 6a, the difference in the curve energy per spin vs temperature for 16x16 and 32x32 is small suggesting that they are both able to model the long range fluctuations. However there is still a slight in the magnetisation per spin vs temperature curve as seen in figure 6b, meaning that possibly the 16x16 lattice is not modelling them particularly well and a 32x32 lattice models them more accurately. If the lattice is to small the effect of one spin flipping is felt more greatly due to the periodic nature of the lattice if this is to small these spins can exert and effect on another. Both of these effects mean that the larger the lattice which is used the batter the prediction as it will model the long range fluctuation better and also eliminate the effect of the spin on its self as this decreases rapidly with distance.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}\quad \mbox{equation 7}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\mbox{show that} \quad C_v = \frac{Var[E]}{k_BT^2} \quad \mbox{equation 8}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
Figures 8,9 and 10 how how the data generated by our selves compares to the C++ data.&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|300px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
[[File:FkLvsTc.png|300px|thumb|figure 13: &amp;lt;math&amp;gt;\frac{1}{lattice length}\quad \mbox{vs} \quad T_{c,L} &amp;lt;/math&amp;gt;]]&lt;br /&gt;
As the Curie temperature changes with the size of the lattice according to equation 7.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{c,L}=\frac{A}{L}+T_{c,\infty}\quad \mbox{equation 8}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
where L is the lattice side length A is a constant &amp;lt;math&amp;gt;T_{c,L}&amp;lt;/math&amp;gt; is the Curie temperature for a given lattice size and &amp;lt;math&amp;gt;T_{c,\infty}&amp;lt;/math&amp;gt; is the Curie temperature at infinity.&lt;br /&gt;
&lt;br /&gt;
The curie temperature of an infinite lattice can be predicted by finding the y intercept of the graph in figure 13. The Curie temperature of an infinite lattice is predicted to be 2.27681069 through the use of the graph. that predicted in literature is 2.16667 to the second order&amp;lt;ref&amp;gt;P. Li, Y. Song, Phys. Lett. A, 2001, &#039;&#039;&#039;289&#039;&#039;&#039;, 147-150.&amp;lt;/ref&amp;gt;. The value predicted by our model gives relatively good agreement. The major sources of error are likely to be due to the size of the lattices being looked at are small meaning that a by only flipping one spin there is a large effect on the average energy per spin. Also a grater number of montecarlo steps where used this would enable a better estimate of the energy to be calculated as the energy will be closer to the average energy for more of the steps.&lt;br /&gt;
&lt;br /&gt;
==final Isinglattic code==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;creating the Ising lattice model&#039;&#039;&#039;&lt;br /&gt;
import numpy as np&lt;br /&gt;
&lt;br /&gt;
class IsingLattice:&lt;br /&gt;
# create statting values so as to keep runing totals of E, E^2, M, M^2 and n_cycles&lt;br /&gt;
    E = 0.0&lt;br /&gt;
    E2 = 0.0&lt;br /&gt;
    M = 0.0&lt;br /&gt;
    M2 = 0.0&lt;br /&gt;
&lt;br /&gt;
    n_cycles = 1&lt;br /&gt;
&lt;br /&gt;
    def __init__(self, n_rows, n_cols):&lt;br /&gt;
        self.n_rows = n_rows #number of rows in the lattise&lt;br /&gt;
        self.n_cols = n_cols #number of colums in the lattise&lt;br /&gt;
        self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols)) #generate random spins for each lattice site&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
        &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
    &lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        #returnes energy and magentisation of resulting state&lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
&lt;br /&gt;
    def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        #rerurnes E,E^2,M,M^2 and step number&lt;br /&gt;
        return E, E2, M, M2, n&lt;br /&gt;
        &lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Refrences==&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796649</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796649"/>
		<updated>2019-11-20T11:33:05Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 12 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 1}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\sum^N_i\sum_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt; is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 3}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 4}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; \mbox{equation 5}&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; \mbox{equation 6}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|250px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|250px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|250px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
From these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. Also as the temperature increases the average magnetisation per spin changes and goes from 1 down to 0 due to the en tropic driving forces. According the equation 7 the gradient of the average energy per spin vs temperature is the heat capacity.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
&lt;br /&gt;
[[File:FkEn vs T.png|200px|thumb|figure 6,a:how energy per spin changes with temperature and lattice size]] &lt;br /&gt;
[[File:FkMag vs T.png|200px|thumb|figure 6,b:how magnetisation per spin changes with temperature and lattice size]] &lt;br /&gt;
these graphs are plotted using the following code&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
x32=np.loadtxt(&#039;32x32.dat&#039;) #extracts the data from the data file&lt;br /&gt;
temp32=x32[:,0] #extracts the temperatures as a list from the data&lt;br /&gt;
E32=x32[:,1] #extracts the average energies from the data  &lt;br /&gt;
EE32=x32[:,2] #extracts the average square of the energies from the data &lt;br /&gt;
M32=x32[:,3] #extracts the average magnetisation from the data&lt;br /&gt;
MM32=x32[:,4] #extracts the average square of the magnetisations from the data &lt;br /&gt;
varE32=(np.array(EE32)-np.array(E32)**2)/(32*32) #calculates the variance of the energies per spin&lt;br /&gt;
varM32=(np.array(MM32)-np.array(M32)**2)/(32*32) #calculates the variance of the magnetisation per spin&lt;br /&gt;
Cv32=heat_capacity(temp32,varE32) #calculates the heat capacities&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,M32/(32*32),label=&#039;32x32&#039;) #plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;magnetisation per spin&#039;)&lt;br /&gt;
pl.title(&#039;how magnetisation per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;mag_vs_T.png&#039;) &lt;br /&gt;
pl.show()&lt;br /&gt;
&lt;br /&gt;
#plots the energy per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,E32/(32*32),label=&#039;32x32&#039;)&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;energy per spin&#039;)&lt;br /&gt;
pl.title(&#039;how energy per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;en_vs_T.png&#039;)&lt;br /&gt;
pl.show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As can be seen in figure 6a, the difference in the curve energy per spin vs temperature for 16x16 and 32x32 is small suggesting that they are both able to model the long range fluctuations. However there is still a slight in the magnetisation per spin vs temperature curve as seen in figure 6b, meaning that possibly the 16x16 lattice is not modelling them particularly well and a 32x32 lattice models them more accurately. If the lattice is to small the effect of one spin flipping is felt more greatly due to the periodic nature of the lattice if this is to small these spins can exert and effect on another. Both of these effects mean that the larger the lattice which is used the batter the prediction as it will model the long range fluctuation better and also eliminate the effect of the spin on its self as this decreases rapidly with distance.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}\quad \mbox{equation 7}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\mbox{show that} \quad C_v = \frac{Var[E]}{k_BT^2} \quad \mbox{equation 8}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
Figures 8,9 and 10 how how the data generated by our selves compares to the C++ data.&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|300px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
[[File:FkLvsTc.png|300px|thumb|figure 13: &amp;lt;math&amp;gt;\frac{1}{lattice length}\quad \mbox{vs} \quad T_{c,L} &amp;lt;/math&amp;gt;]]&lt;br /&gt;
As the Curie temperature changes with the size of the lattice according to equation 7.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{c,L}=\frac{A}{L}+T_{c,\infty}\quad \mbox{equation 7}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
The curie temperature of an infinite lattice can be predicted by finding the y intercept of the graph in figure 13. The Curie temperature of an infinite lattice is predicted to be 2.27681069 through the use of the graph. that predicted in literature is 2.16667 to the second order&amp;lt;ref&amp;gt;P. Li, Y. Song, Phys. Lett. A, 2001, &#039;&#039;&#039;289&#039;&#039;&#039;, 147-150.&amp;lt;/ref&amp;gt;. The value predicted by our model gives relatively good agreement. The major sources of error are likely to be due to the size of the lattices being looked at are small meaning that a by only flipping one spin there is a large effect on the average energy per spin. Also a grater number of montecarlo steps where used this would enable a better estimate of the energy to be calculated as the energy will be closer to the average energy for more of the steps.&lt;br /&gt;
==final Isinglattic code==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;creating the Ising lattice model&#039;&#039;&#039;&lt;br /&gt;
import numpy as np&lt;br /&gt;
&lt;br /&gt;
class IsingLattice:&lt;br /&gt;
# create statting values so as to keep runing totals of E, E^2, M, M^2 and n_cycles&lt;br /&gt;
    E = 0.0&lt;br /&gt;
    E2 = 0.0&lt;br /&gt;
    M = 0.0&lt;br /&gt;
    M2 = 0.0&lt;br /&gt;
&lt;br /&gt;
    n_cycles = 1&lt;br /&gt;
&lt;br /&gt;
    def __init__(self, n_rows, n_cols):&lt;br /&gt;
        self.n_rows = n_rows #number of rows in the lattise&lt;br /&gt;
        self.n_cols = n_cols #number of colums in the lattise&lt;br /&gt;
        self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols)) #generate random spins for each lattice site&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
        &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
    &lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        #returnes energy and magentisation of resulting state&lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
&lt;br /&gt;
    def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        #rerurnes E,E^2,M,M^2 and step number&lt;br /&gt;
        return E, E2, M, M2, n&lt;br /&gt;
        &lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Refrences==&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796642</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796642"/>
		<updated>2019-11-20T11:28:33Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 1}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\sum^N_i\sum_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt; is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 3}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 4}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; \mbox{equation 5}&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; \mbox{equation 6}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|250px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|250px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|250px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
From these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
&lt;br /&gt;
[[File:FkEn vs T.png|200px|thumb|figure 6,a:how energy per spin changes with temperature and lattice size]] &lt;br /&gt;
[[File:FkMag vs T.png|200px|thumb|figure 6,b:how magnetisation per spin changes with temperature and lattice size]] &lt;br /&gt;
these graphs are plotted using the following code&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
x32=np.loadtxt(&#039;32x32.dat&#039;) #extracts the data from the data file&lt;br /&gt;
temp32=x32[:,0] #extracts the temperatures as a list from the data&lt;br /&gt;
E32=x32[:,1] #extracts the average energies from the data  &lt;br /&gt;
EE32=x32[:,2] #extracts the average square of the energies from the data &lt;br /&gt;
M32=x32[:,3] #extracts the average magnetisation from the data&lt;br /&gt;
MM32=x32[:,4] #extracts the average square of the magnetisations from the data &lt;br /&gt;
varE32=(np.array(EE32)-np.array(E32)**2)/(32*32) #calculates the variance of the energies per spin&lt;br /&gt;
varM32=(np.array(MM32)-np.array(M32)**2)/(32*32) #calculates the variance of the magnetisation per spin&lt;br /&gt;
Cv32=heat_capacity(temp32,varE32) #calculates the heat capacities&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,M32/(32*32),label=&#039;32x32&#039;) #plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;magnetisation per spin&#039;)&lt;br /&gt;
pl.title(&#039;how magnetisation per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;mag_vs_T.png&#039;) &lt;br /&gt;
pl.show()&lt;br /&gt;
&lt;br /&gt;
#plots the energy per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,E32/(32*32),label=&#039;32x32&#039;)&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;energy per spin&#039;)&lt;br /&gt;
pl.title(&#039;how energy per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;en_vs_T.png&#039;)&lt;br /&gt;
pl.show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As can be seen in figure 6a, the difference in the curve energy per spin vs temperature for 16x16 and 32x32 is small suggesting that they are both able to model the long range fluctuations. However there is still a slight in the magnetisation per spin vs temperature curve as seen in figure 6b, meaning that possibly the 16x16 lattice is not modelling them particularly well and a 32x32 lattice models them more accurately. If the lattice is to small the effect of one spin flipping is felt more greatly due to the periodic nature of the lattice if this is to small these spins can exert and effect on another. Both of these effects mean that the larger the lattice which is used the batter the prediction as it will model the long range fluctuation better and also eliminate the effect of the spin on its self as this decreases rapidly with distance.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}\quad \mbox{equation 7}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\mbox{show that} \quad C_v = \frac{Var[E]}{k_BT^2} \quad \mbox{equation 8}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
Figures 8,9 and 10 how how the data generated by our selves compares to the C++ data.&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|300px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
[[File:FkLvsTc.png|300px|thumb|figure 13: &amp;lt;math&amp;gt;\frac{1}{lattice length}\quad \mbox{vs} \quad T_{c,L} &amp;lt;/math&amp;gt;]]&lt;br /&gt;
As the Curie temperature changes with the size of the lattice according to equation 7.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{c,L}=\frac{A}{L}+T_{c,\infty}\quad \mbox{equation 7}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
The curie temperature of an infinite lattice can be predicted by finding the y intercept of the graph in figure 13. The Curie temperature of an infinite lattice is predicted to be 2.27681069 through the use of the graph. that predicted in literature is 2.16667 to the second order&amp;lt;ref&amp;gt;P. Li, Y. Song, Phys. Lett. A, 2001, &#039;&#039;&#039;289&#039;&#039;&#039;, 147-150.&amp;lt;/ref&amp;gt;. The value predicted by our model gives relatively good agreement. The major sources of error are likely to be due to the size of the lattices being looked at are small meaning that a by only flipping one spin there is a large effect on the average energy per spin. Also a grater number of montecarlo steps where used this would enable a better estimate of the energy to be calculated as the energy will be closer to the average energy for more of the steps.&lt;br /&gt;
==final Isinglattic code==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;creating the Ising lattice model&#039;&#039;&#039;&lt;br /&gt;
import numpy as np&lt;br /&gt;
&lt;br /&gt;
class IsingLattice:&lt;br /&gt;
# create statting values so as to keep runing totals of E, E^2, M, M^2 and n_cycles&lt;br /&gt;
    E = 0.0&lt;br /&gt;
    E2 = 0.0&lt;br /&gt;
    M = 0.0&lt;br /&gt;
    M2 = 0.0&lt;br /&gt;
&lt;br /&gt;
    n_cycles = 1&lt;br /&gt;
&lt;br /&gt;
    def __init__(self, n_rows, n_cols):&lt;br /&gt;
        self.n_rows = n_rows #number of rows in the lattise&lt;br /&gt;
        self.n_cols = n_cols #number of colums in the lattise&lt;br /&gt;
        self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols)) #generate random spins for each lattice site&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
        &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
    &lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        #returnes energy and magentisation of resulting state&lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
&lt;br /&gt;
    def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        #rerurnes E,E^2,M,M^2 and step number&lt;br /&gt;
        return E, E2, M, M2, n&lt;br /&gt;
        &lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Refrences==&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796641</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796641"/>
		<updated>2019-11-20T11:28:11Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 14 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 1}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\sum^N_i\sum_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt; is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 3}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 4}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; \mbox{equation 5}&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; \mbox{equation 6}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|250px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|250px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|250px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
From these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
&lt;br /&gt;
[[File:FkEn vs T.png|200px|thumb|figure 6,a:how energy per spin changes with temperature and lattice size]] &lt;br /&gt;
[[File:FkMag vs T.png|200px|thumb|figure 6,b:how magnetisation per spin changes with temperature and lattice size]] &lt;br /&gt;
these graphs are plotted using the following code&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
x32=np.loadtxt(&#039;32x32.dat&#039;) #extracts the data from the data file&lt;br /&gt;
temp32=x32[:,0] #extracts the temperatures as a list from the data&lt;br /&gt;
E32=x32[:,1] #extracts the average energies from the data  &lt;br /&gt;
EE32=x32[:,2] #extracts the average square of the energies from the data &lt;br /&gt;
M32=x32[:,3] #extracts the average magnetisation from the data&lt;br /&gt;
MM32=x32[:,4] #extracts the average square of the magnetisations from the data &lt;br /&gt;
varE32=(np.array(EE32)-np.array(E32)**2)/(32*32) #calculates the variance of the energies per spin&lt;br /&gt;
varM32=(np.array(MM32)-np.array(M32)**2)/(32*32) #calculates the variance of the magnetisation per spin&lt;br /&gt;
Cv32=heat_capacity(temp32,varE32) #calculates the heat capacities&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,M32/(32*32),label=&#039;32x32&#039;) #plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;magnetisation per spin&#039;)&lt;br /&gt;
pl.title(&#039;how magnetisation per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;mag_vs_T.png&#039;) &lt;br /&gt;
pl.show()&lt;br /&gt;
&lt;br /&gt;
#plots the energy per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,E32/(32*32),label=&#039;32x32&#039;)&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;energy per spin&#039;)&lt;br /&gt;
pl.title(&#039;how energy per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;en_vs_T.png&#039;)&lt;br /&gt;
pl.show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As can be seen in figure 6a, the difference in the curve energy per spin vs temperature for 16x16 and 32x32 is small suggesting that they are both able to model the long range fluctuations. However there is still a slight in the magnetisation per spin vs temperature curve as seen in figure 6b, meaning that possibly the 16x16 lattice is not modelling them particularly well and a 32x32 lattice models them more accurately. If the lattice is to small the effect of one spin flipping is felt more greatly due to the periodic nature of the lattice if this is to small these spins can exert and effect on another. Both of these effects mean that the larger the lattice which is used the batter the prediction as it will model the long range fluctuation better and also eliminate the effect of the spin on its self as this decreases rapidly with distance.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}\quad \mbox{equation 7}&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;\mbox{show that} C_v = \frac{Var[E]}{k_BT^2} \mbox{equation 8}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
Figures 8,9 and 10 how how the data generated by our selves compares to the C++ data.&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|300px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
[[File:FkLvsTc.png|300px|thumb|figure 13: &amp;lt;math&amp;gt;\frac{1}{lattice length}\quad \mbox{vs} \quad T_{c,L} &amp;lt;/math&amp;gt;]]&lt;br /&gt;
As the Curie temperature changes with the size of the lattice according to equation 7.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{c,L}=\frac{A}{L}+T_{c,\infty}\quad \mbox{equation 7}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
The curie temperature of an infinite lattice can be predicted by finding the y intercept of the graph in figure 13. The Curie temperature of an infinite lattice is predicted to be 2.27681069 through the use of the graph. that predicted in literature is 2.16667 to the second order&amp;lt;ref&amp;gt;P. Li, Y. Song, Phys. Lett. A, 2001, &#039;&#039;&#039;289&#039;&#039;&#039;, 147-150.&amp;lt;/ref&amp;gt;. The value predicted by our model gives relatively good agreement. The major sources of error are likely to be due to the size of the lattices being looked at are small meaning that a by only flipping one spin there is a large effect on the average energy per spin. Also a grater number of montecarlo steps where used this would enable a better estimate of the energy to be calculated as the energy will be closer to the average energy for more of the steps.&lt;br /&gt;
==final Isinglattic code==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;creating the Ising lattice model&#039;&#039;&#039;&lt;br /&gt;
import numpy as np&lt;br /&gt;
&lt;br /&gt;
class IsingLattice:&lt;br /&gt;
# create statting values so as to keep runing totals of E, E^2, M, M^2 and n_cycles&lt;br /&gt;
    E = 0.0&lt;br /&gt;
    E2 = 0.0&lt;br /&gt;
    M = 0.0&lt;br /&gt;
    M2 = 0.0&lt;br /&gt;
&lt;br /&gt;
    n_cycles = 1&lt;br /&gt;
&lt;br /&gt;
    def __init__(self, n_rows, n_cols):&lt;br /&gt;
        self.n_rows = n_rows #number of rows in the lattise&lt;br /&gt;
        self.n_cols = n_cols #number of colums in the lattise&lt;br /&gt;
        self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols)) #generate random spins for each lattice site&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
        &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
    &lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        #returnes energy and magentisation of resulting state&lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
&lt;br /&gt;
    def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        #rerurnes E,E^2,M,M^2 and step number&lt;br /&gt;
        return E, E2, M, M2, n&lt;br /&gt;
        &lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Refrences==&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796639</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796639"/>
		<updated>2019-11-20T11:25:50Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 11 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 1}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\sum^N_i\sum_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt; is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 3}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 4}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; \mbox{equation 5}&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; \mbox{equation 6}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|250px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|250px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|250px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
From these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
&lt;br /&gt;
[[File:FkEn vs T.png|200px|thumb|figure 6,a:how energy per spin changes with temperature and lattice size]] &lt;br /&gt;
[[File:FkMag vs T.png|200px|thumb|figure 6,b:how magnetisation per spin changes with temperature and lattice size]] &lt;br /&gt;
these graphs are plotted using the following code&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
x32=np.loadtxt(&#039;32x32.dat&#039;) #extracts the data from the data file&lt;br /&gt;
temp32=x32[:,0] #extracts the temperatures as a list from the data&lt;br /&gt;
E32=x32[:,1] #extracts the average energies from the data  &lt;br /&gt;
EE32=x32[:,2] #extracts the average square of the energies from the data &lt;br /&gt;
M32=x32[:,3] #extracts the average magnetisation from the data&lt;br /&gt;
MM32=x32[:,4] #extracts the average square of the magnetisations from the data &lt;br /&gt;
varE32=(np.array(EE32)-np.array(E32)**2)/(32*32) #calculates the variance of the energies per spin&lt;br /&gt;
varM32=(np.array(MM32)-np.array(M32)**2)/(32*32) #calculates the variance of the magnetisation per spin&lt;br /&gt;
Cv32=heat_capacity(temp32,varE32) #calculates the heat capacities&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,M32/(32*32),label=&#039;32x32&#039;) #plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;magnetisation per spin&#039;)&lt;br /&gt;
pl.title(&#039;how magnetisation per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;mag_vs_T.png&#039;) &lt;br /&gt;
pl.show()&lt;br /&gt;
&lt;br /&gt;
#plots the energy per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,E32/(32*32),label=&#039;32x32&#039;)&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;energy per spin&#039;)&lt;br /&gt;
pl.title(&#039;how energy per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;en_vs_T.png&#039;)&lt;br /&gt;
pl.show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As can be seen in figure 6a, the difference in the curve energy per spin vs temperature for 16x16 and 32x32 is small suggesting that they are both able to model the long range fluctuations. However there is still a slight in the magnetisation per spin vs temperature curve as seen in figure 6b, meaning that possibly the 16x16 lattice is not modelling them particularly well and a 32x32 lattice models them more accurately. If the lattice is to small the effect of one spin flipping is felt more greatly due to the periodic nature of the lattice if this is to small these spins can exert and effect on another. Both of these effects mean that the larger the lattice which is used the batter the prediction as it will model the long range fluctuation better and also eliminate the effect of the spin on its self as this decreases rapidly with distance.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
Figures 8,9 and 10 how how the data generated by our selves compares to the C++ data.&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|300px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
[[File:FkLvsTc.png|300px|thumb|figure 13: &amp;lt;math&amp;gt;\frac{1}{lattice length}\quad \mbox{vs} \quad T_{c,L} &amp;lt;/math&amp;gt;]]&lt;br /&gt;
As the Curie temperature changes with the size of the lattice according to equation 7.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{c,L}=\frac{A}{L}+T_{c,\infty}\quad \mbox{equation 7}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
The curie temperature of an infinite lattice can be predicted by finding the y intercept of the graph in figure 13. The Curie temperature of an infinite lattice is predicted to be 2.27681069 through the use of the graph. that predicted in literature is 2.16667 to the second order&amp;lt;ref&amp;gt;P. Li, Y. Song, Phys. Lett. A, 2001, &#039;&#039;&#039;289&#039;&#039;&#039;, 147-150.&amp;lt;/ref&amp;gt;. The value predicted by our model gives relatively good agreement. The major sources of error are likely to be due to the size of the lattices being looked at are small meaning that a by only flipping one spin there is a large effect on the average energy per spin. Also a grater number of montecarlo steps where used this would enable a better estimate of the energy to be calculated as the energy will be closer to the average energy for more of the steps.&lt;br /&gt;
==final Isinglattic code==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;creating the Ising lattice model&#039;&#039;&#039;&lt;br /&gt;
import numpy as np&lt;br /&gt;
&lt;br /&gt;
class IsingLattice:&lt;br /&gt;
# create statting values so as to keep runing totals of E, E^2, M, M^2 and n_cycles&lt;br /&gt;
    E = 0.0&lt;br /&gt;
    E2 = 0.0&lt;br /&gt;
    M = 0.0&lt;br /&gt;
    M2 = 0.0&lt;br /&gt;
&lt;br /&gt;
    n_cycles = 1&lt;br /&gt;
&lt;br /&gt;
    def __init__(self, n_rows, n_cols):&lt;br /&gt;
        self.n_rows = n_rows #number of rows in the lattise&lt;br /&gt;
        self.n_cols = n_cols #number of colums in the lattise&lt;br /&gt;
        self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols)) #generate random spins for each lattice site&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
        &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
    &lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        #returnes energy and magentisation of resulting state&lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
&lt;br /&gt;
    def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        #rerurnes E,E^2,M,M^2 and step number&lt;br /&gt;
        return E, E2, M, M2, n&lt;br /&gt;
        &lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Refrences==&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796638</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796638"/>
		<updated>2019-11-20T11:25:24Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 11 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 1}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\sum^N_i\sum_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt; is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 3}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 4}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; \mbox{equation 5}&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; \mbox{equation 6}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|250px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|250px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|250px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
From these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
&lt;br /&gt;
[[File:FkEn vs T.png|200px|thumb|figure 6,a:how energy per spin changes with temperature and lattice size]] &lt;br /&gt;
[[File:FkMag vs T.png|200px|thumb|figure 6,b:how magnetisation per spin changes with temperature and lattice size]] &lt;br /&gt;
these graphs are plotted using the following code&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
x32=np.loadtxt(&#039;32x32.dat&#039;) #extracts the data from the data file&lt;br /&gt;
temp32=x32[:,0] #extracts the temperatures as a list from the data&lt;br /&gt;
E32=x32[:,1] #extracts the average energies from the data  &lt;br /&gt;
EE32=x32[:,2] #extracts the average square of the energies from the data &lt;br /&gt;
M32=x32[:,3] #extracts the average magnetisation from the data&lt;br /&gt;
MM32=x32[:,4] #extracts the average square of the magnetisations from the data &lt;br /&gt;
varE32=(np.array(EE32)-np.array(E32)**2)/(32*32) #calculates the variance of the energies per spin&lt;br /&gt;
varM32=(np.array(MM32)-np.array(M32)**2)/(32*32) #calculates the variance of the magnetisation per spin&lt;br /&gt;
Cv32=heat_capacity(temp32,varE32) #calculates the heat capacities&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,M32/(32*32),label=&#039;32x32&#039;) #plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;magnetisation per spin&#039;)&lt;br /&gt;
pl.title(&#039;how magnetisation per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;mag_vs_T.png&#039;) &lt;br /&gt;
pl.show()&lt;br /&gt;
&lt;br /&gt;
#plots the energy per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,E32/(32*32),label=&#039;32x32&#039;)&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;energy per spin&#039;)&lt;br /&gt;
pl.title(&#039;how energy per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;en_vs_T.png&#039;)&lt;br /&gt;
pl.show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As can be seen in figure 6a, the difference in the curve energy per spin vs temperature for 16x16 and 32x32 is small suggesting that they are both able to model the long range fluctuations. However there is still a slight in the magnetisation per spin vs temperature curve as seen in figure 6b, meaning that possibly the 16x16 lattice is not modelling them particularly well and a 32x32 lattice models them more accurately. If the lattice is to small the effect of one spin flipping is felt more greatly due to the periodic nature of the lattice if this is to small these spins can exert and effect on another. Both of these effects mean that the larger the lattice which is used the batter the prediction as it will model the long range fluctuation better and also eliminate the effect of the spin on its self as this decreases rapidly with distance.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
Figures 8,9 and 10 how how the data generated by our selves compares to the C++ data.&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|300px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
[[File:FkLvsTc.png|300px|thumb|figure 13: &amp;lt;math&amp;gt;\frac{1}{lattice length}\quad \mbox{vs} \quad T_{c,L} &amp;lt;/math&amp;gt;]]&lt;br /&gt;
As the Curie temperature changes with the size of the lattice according to equation 7.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{c,L}=\frac{A}{L}+T_{c,\infty}\quad \mbox{equation 7}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
The curie temperature of an infinite lattice can be predicted by finding the y intercept of the graph in figure 13. The Curie temperature of an infinite lattice is predicted to be 2.27681069 through the use of the graph. that predicted in literature is 2.16667 to the second order&amp;lt;ref&amp;gt;P. Li, Y. Song, Phys. Lett. A, 2001, &#039;&#039;&#039;289&#039;&#039;&#039;, 147-150.&amp;lt;/ref&amp;gt;. The value predicted by our model gives relatively good agreement. The major sources of error are likely to be due to the size of the lattices being looked at are small meaning that a by only flipping one spin there is a large effect on the average energy per spin. Also a grater number of montecarlo steps where used this would enable a better estimate of the energy to be calculated as the energy will be closer to the average energy for more of the steps.&lt;br /&gt;
==final Isinglattic code==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;creating the Ising lattice model&#039;&#039;&#039;&lt;br /&gt;
import numpy as np&lt;br /&gt;
&lt;br /&gt;
class IsingLattice:&lt;br /&gt;
# create statting values so as to keep runing totals of E, E^2, M, M^2 and n_cycles&lt;br /&gt;
    E = 0.0&lt;br /&gt;
    E2 = 0.0&lt;br /&gt;
    M = 0.0&lt;br /&gt;
    M2 = 0.0&lt;br /&gt;
&lt;br /&gt;
    n_cycles = 1&lt;br /&gt;
&lt;br /&gt;
    def __init__(self, n_rows, n_cols):&lt;br /&gt;
        self.n_rows = n_rows #number of rows in the lattise&lt;br /&gt;
        self.n_cols = n_cols #number of colums in the lattise&lt;br /&gt;
        self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols)) #generate random spins for each lattice site&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
        &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
    &lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        #returnes energy and magentisation of resulting state&lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
&lt;br /&gt;
    def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        #rerurnes E,E^2,M,M^2 and step number&lt;br /&gt;
        return E, E2, M, M2, n&lt;br /&gt;
        &lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Refrences==&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796637</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796637"/>
		<updated>2019-11-20T11:24:39Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 6 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 1}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\sum^N_i\sum_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt; is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 3}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 4}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; \mbox{equation 5}&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; \mbox{equation 6}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
&lt;br /&gt;
[[File:FkEn vs T.png|200px|thumb|figure 6,a:how energy per spin changes with temperature and lattice size]] &lt;br /&gt;
[[File:FkMag vs T.png|200px|thumb|figure 6,b:how magnetisation per spin changes with temperature and lattice size]] &lt;br /&gt;
these graphs are plotted using the following code&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
x32=np.loadtxt(&#039;32x32.dat&#039;) #extracts the data from the data file&lt;br /&gt;
temp32=x32[:,0] #extracts the temperatures as a list from the data&lt;br /&gt;
E32=x32[:,1] #extracts the average energies from the data  &lt;br /&gt;
EE32=x32[:,2] #extracts the average square of the energies from the data &lt;br /&gt;
M32=x32[:,3] #extracts the average magnetisation from the data&lt;br /&gt;
MM32=x32[:,4] #extracts the average square of the magnetisations from the data &lt;br /&gt;
varE32=(np.array(EE32)-np.array(E32)**2)/(32*32) #calculates the variance of the energies per spin&lt;br /&gt;
varM32=(np.array(MM32)-np.array(M32)**2)/(32*32) #calculates the variance of the magnetisation per spin&lt;br /&gt;
Cv32=heat_capacity(temp32,varE32) #calculates the heat capacities&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,M32/(32*32),label=&#039;32x32&#039;) #plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;magnetisation per spin&#039;)&lt;br /&gt;
pl.title(&#039;how magnetisation per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;mag_vs_T.png&#039;) &lt;br /&gt;
pl.show()&lt;br /&gt;
&lt;br /&gt;
#plots the energy per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,E32/(32*32),label=&#039;32x32&#039;)&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;energy per spin&#039;)&lt;br /&gt;
pl.title(&#039;how energy per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;en_vs_T.png&#039;)&lt;br /&gt;
pl.show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As can be seen in figure 6a, the difference in the curve energy per spin vs temperature for 16x16 and 32x32 is small suggesting that they are both able to model the long range fluctuations. However there is still a slight in the magnetisation per spin vs temperature curve as seen in figure 6b, meaning that possibly the 16x16 lattice is not modelling them particularly well and a 32x32 lattice models them more accurately. If the lattice is to small the effect of one spin flipping is felt more greatly due to the periodic nature of the lattice if this is to small these spins can exert and effect on another. Both of these effects mean that the larger the lattice which is used the batter the prediction as it will model the long range fluctuation better and also eliminate the effect of the spin on its self as this decreases rapidly with distance.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
Figures 8,9 and 10 how how the data generated by our selves compares to the C++ data.&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|300px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
[[File:FkLvsTc.png|300px|thumb|figure 13: &amp;lt;math&amp;gt;\frac{1}{lattice length}\quad \mbox{vs} \quad T_{c,L} &amp;lt;/math&amp;gt;]]&lt;br /&gt;
As the Curie temperature changes with the size of the lattice according to equation 7.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{c,L}=\frac{A}{L}+T_{c,\infty}\quad \mbox{equation 7}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
The curie temperature of an infinite lattice can be predicted by finding the y intercept of the graph in figure 13. The Curie temperature of an infinite lattice is predicted to be 2.27681069 through the use of the graph. that predicted in literature is 2.16667 to the second order&amp;lt;ref&amp;gt;P. Li, Y. Song, Phys. Lett. A, 2001, &#039;&#039;&#039;289&#039;&#039;&#039;, 147-150.&amp;lt;/ref&amp;gt;. The value predicted by our model gives relatively good agreement. The major sources of error are likely to be due to the size of the lattices being looked at are small meaning that a by only flipping one spin there is a large effect on the average energy per spin. Also a grater number of montecarlo steps where used this would enable a better estimate of the energy to be calculated as the energy will be closer to the average energy for more of the steps.&lt;br /&gt;
==final Isinglattic code==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;creating the Ising lattice model&#039;&#039;&#039;&lt;br /&gt;
import numpy as np&lt;br /&gt;
&lt;br /&gt;
class IsingLattice:&lt;br /&gt;
# create statting values so as to keep runing totals of E, E^2, M, M^2 and n_cycles&lt;br /&gt;
    E = 0.0&lt;br /&gt;
    E2 = 0.0&lt;br /&gt;
    M = 0.0&lt;br /&gt;
    M2 = 0.0&lt;br /&gt;
&lt;br /&gt;
    n_cycles = 1&lt;br /&gt;
&lt;br /&gt;
    def __init__(self, n_rows, n_cols):&lt;br /&gt;
        self.n_rows = n_rows #number of rows in the lattise&lt;br /&gt;
        self.n_cols = n_cols #number of colums in the lattise&lt;br /&gt;
        self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols)) #generate random spins for each lattice site&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
        &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
    &lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        #returnes energy and magentisation of resulting state&lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
&lt;br /&gt;
    def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        #rerurnes E,E^2,M,M^2 and step number&lt;br /&gt;
        return E, E2, M, M2, n&lt;br /&gt;
        &lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Refrences==&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796636</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796636"/>
		<updated>2019-11-20T11:24:11Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* task 3 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 1}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\sum^N_i\sum_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt; is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 3}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 4}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
&lt;br /&gt;
[[File:FkEn vs T.png|200px|thumb|figure 6,a:how energy per spin changes with temperature and lattice size]] &lt;br /&gt;
[[File:FkMag vs T.png|200px|thumb|figure 6,b:how magnetisation per spin changes with temperature and lattice size]] &lt;br /&gt;
these graphs are plotted using the following code&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
x32=np.loadtxt(&#039;32x32.dat&#039;) #extracts the data from the data file&lt;br /&gt;
temp32=x32[:,0] #extracts the temperatures as a list from the data&lt;br /&gt;
E32=x32[:,1] #extracts the average energies from the data  &lt;br /&gt;
EE32=x32[:,2] #extracts the average square of the energies from the data &lt;br /&gt;
M32=x32[:,3] #extracts the average magnetisation from the data&lt;br /&gt;
MM32=x32[:,4] #extracts the average square of the magnetisations from the data &lt;br /&gt;
varE32=(np.array(EE32)-np.array(E32)**2)/(32*32) #calculates the variance of the energies per spin&lt;br /&gt;
varM32=(np.array(MM32)-np.array(M32)**2)/(32*32) #calculates the variance of the magnetisation per spin&lt;br /&gt;
Cv32=heat_capacity(temp32,varE32) #calculates the heat capacities&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,M32/(32*32),label=&#039;32x32&#039;) #plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;magnetisation per spin&#039;)&lt;br /&gt;
pl.title(&#039;how magnetisation per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;mag_vs_T.png&#039;) &lt;br /&gt;
pl.show()&lt;br /&gt;
&lt;br /&gt;
#plots the energy per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,E32/(32*32),label=&#039;32x32&#039;)&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;energy per spin&#039;)&lt;br /&gt;
pl.title(&#039;how energy per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;en_vs_T.png&#039;)&lt;br /&gt;
pl.show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As can be seen in figure 6a, the difference in the curve energy per spin vs temperature for 16x16 and 32x32 is small suggesting that they are both able to model the long range fluctuations. However there is still a slight in the magnetisation per spin vs temperature curve as seen in figure 6b, meaning that possibly the 16x16 lattice is not modelling them particularly well and a 32x32 lattice models them more accurately. If the lattice is to small the effect of one spin flipping is felt more greatly due to the periodic nature of the lattice if this is to small these spins can exert and effect on another. Both of these effects mean that the larger the lattice which is used the batter the prediction as it will model the long range fluctuation better and also eliminate the effect of the spin on its self as this decreases rapidly with distance.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
Figures 8,9 and 10 how how the data generated by our selves compares to the C++ data.&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|300px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
[[File:FkLvsTc.png|300px|thumb|figure 13: &amp;lt;math&amp;gt;\frac{1}{lattice length}\quad \mbox{vs} \quad T_{c,L} &amp;lt;/math&amp;gt;]]&lt;br /&gt;
As the Curie temperature changes with the size of the lattice according to equation 7.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{c,L}=\frac{A}{L}+T_{c,\infty}\quad \mbox{equation 7}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
The curie temperature of an infinite lattice can be predicted by finding the y intercept of the graph in figure 13. The Curie temperature of an infinite lattice is predicted to be 2.27681069 through the use of the graph. that predicted in literature is 2.16667 to the second order&amp;lt;ref&amp;gt;P. Li, Y. Song, Phys. Lett. A, 2001, &#039;&#039;&#039;289&#039;&#039;&#039;, 147-150.&amp;lt;/ref&amp;gt;. The value predicted by our model gives relatively good agreement. The major sources of error are likely to be due to the size of the lattices being looked at are small meaning that a by only flipping one spin there is a large effect on the average energy per spin. Also a grater number of montecarlo steps where used this would enable a better estimate of the energy to be calculated as the energy will be closer to the average energy for more of the steps.&lt;br /&gt;
==final Isinglattic code==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;creating the Ising lattice model&#039;&#039;&#039;&lt;br /&gt;
import numpy as np&lt;br /&gt;
&lt;br /&gt;
class IsingLattice:&lt;br /&gt;
# create statting values so as to keep runing totals of E, E^2, M, M^2 and n_cycles&lt;br /&gt;
    E = 0.0&lt;br /&gt;
    E2 = 0.0&lt;br /&gt;
    M = 0.0&lt;br /&gt;
    M2 = 0.0&lt;br /&gt;
&lt;br /&gt;
    n_cycles = 1&lt;br /&gt;
&lt;br /&gt;
    def __init__(self, n_rows, n_cols):&lt;br /&gt;
        self.n_rows = n_rows #number of rows in the lattise&lt;br /&gt;
        self.n_cols = n_cols #number of colums in the lattise&lt;br /&gt;
        self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols)) #generate random spins for each lattice site&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
        &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
    &lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        #returnes energy and magentisation of resulting state&lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
&lt;br /&gt;
    def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        #rerurnes E,E^2,M,M^2 and step number&lt;br /&gt;
        return E, E2, M, M2, n&lt;br /&gt;
        &lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Refrences==&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796635</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796635"/>
		<updated>2019-11-20T11:23:51Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* task 1 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 1}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\sum^N_i\sum_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt; is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;\mbox{equation 3}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
&lt;br /&gt;
[[File:FkEn vs T.png|200px|thumb|figure 6,a:how energy per spin changes with temperature and lattice size]] &lt;br /&gt;
[[File:FkMag vs T.png|200px|thumb|figure 6,b:how magnetisation per spin changes with temperature and lattice size]] &lt;br /&gt;
these graphs are plotted using the following code&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
x32=np.loadtxt(&#039;32x32.dat&#039;) #extracts the data from the data file&lt;br /&gt;
temp32=x32[:,0] #extracts the temperatures as a list from the data&lt;br /&gt;
E32=x32[:,1] #extracts the average energies from the data  &lt;br /&gt;
EE32=x32[:,2] #extracts the average square of the energies from the data &lt;br /&gt;
M32=x32[:,3] #extracts the average magnetisation from the data&lt;br /&gt;
MM32=x32[:,4] #extracts the average square of the magnetisations from the data &lt;br /&gt;
varE32=(np.array(EE32)-np.array(E32)**2)/(32*32) #calculates the variance of the energies per spin&lt;br /&gt;
varM32=(np.array(MM32)-np.array(M32)**2)/(32*32) #calculates the variance of the magnetisation per spin&lt;br /&gt;
Cv32=heat_capacity(temp32,varE32) #calculates the heat capacities&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,M32/(32*32),label=&#039;32x32&#039;) #plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;magnetisation per spin&#039;)&lt;br /&gt;
pl.title(&#039;how magnetisation per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;mag_vs_T.png&#039;) &lt;br /&gt;
pl.show()&lt;br /&gt;
&lt;br /&gt;
#plots the energy per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,E32/(32*32),label=&#039;32x32&#039;)&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;energy per spin&#039;)&lt;br /&gt;
pl.title(&#039;how energy per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;en_vs_T.png&#039;)&lt;br /&gt;
pl.show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As can be seen in figure 6a, the difference in the curve energy per spin vs temperature for 16x16 and 32x32 is small suggesting that they are both able to model the long range fluctuations. However there is still a slight in the magnetisation per spin vs temperature curve as seen in figure 6b, meaning that possibly the 16x16 lattice is not modelling them particularly well and a 32x32 lattice models them more accurately. If the lattice is to small the effect of one spin flipping is felt more greatly due to the periodic nature of the lattice if this is to small these spins can exert and effect on another. Both of these effects mean that the larger the lattice which is used the batter the prediction as it will model the long range fluctuation better and also eliminate the effect of the spin on its self as this decreases rapidly with distance.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
Figures 8,9 and 10 how how the data generated by our selves compares to the C++ data.&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|300px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
[[File:FkLvsTc.png|300px|thumb|figure 13: &amp;lt;math&amp;gt;\frac{1}{lattice length}\quad \mbox{vs} \quad T_{c,L} &amp;lt;/math&amp;gt;]]&lt;br /&gt;
As the Curie temperature changes with the size of the lattice according to equation 7.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{c,L}=\frac{A}{L}+T_{c,\infty}\quad \mbox{equation 7}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
The curie temperature of an infinite lattice can be predicted by finding the y intercept of the graph in figure 13. The Curie temperature of an infinite lattice is predicted to be 2.27681069 through the use of the graph. that predicted in literature is 2.16667 to the second order&amp;lt;ref&amp;gt;P. Li, Y. Song, Phys. Lett. A, 2001, &#039;&#039;&#039;289&#039;&#039;&#039;, 147-150.&amp;lt;/ref&amp;gt;. The value predicted by our model gives relatively good agreement. The major sources of error are likely to be due to the size of the lattices being looked at are small meaning that a by only flipping one spin there is a large effect on the average energy per spin. Also a grater number of montecarlo steps where used this would enable a better estimate of the energy to be calculated as the energy will be closer to the average energy for more of the steps.&lt;br /&gt;
==final Isinglattic code==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;creating the Ising lattice model&#039;&#039;&#039;&lt;br /&gt;
import numpy as np&lt;br /&gt;
&lt;br /&gt;
class IsingLattice:&lt;br /&gt;
# create statting values so as to keep runing totals of E, E^2, M, M^2 and n_cycles&lt;br /&gt;
    E = 0.0&lt;br /&gt;
    E2 = 0.0&lt;br /&gt;
    M = 0.0&lt;br /&gt;
    M2 = 0.0&lt;br /&gt;
&lt;br /&gt;
    n_cycles = 1&lt;br /&gt;
&lt;br /&gt;
    def __init__(self, n_rows, n_cols):&lt;br /&gt;
        self.n_rows = n_rows #number of rows in the lattise&lt;br /&gt;
        self.n_cols = n_cols #number of colums in the lattise&lt;br /&gt;
        self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols)) #generate random spins for each lattice site&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
        &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
    &lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        #returnes energy and magentisation of resulting state&lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
&lt;br /&gt;
    def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        #rerurnes E,E^2,M,M^2 and step number&lt;br /&gt;
        return E, E2, M, M2, n&lt;br /&gt;
        &lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Refrences==&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796633</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796633"/>
		<updated>2019-11-20T11:23:02Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* task 1 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\sum^N_i\sum_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt; is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
&lt;br /&gt;
[[File:FkEn vs T.png|200px|thumb|figure 6,a:how energy per spin changes with temperature and lattice size]] &lt;br /&gt;
[[File:FkMag vs T.png|200px|thumb|figure 6,b:how magnetisation per spin changes with temperature and lattice size]] &lt;br /&gt;
these graphs are plotted using the following code&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
x32=np.loadtxt(&#039;32x32.dat&#039;) #extracts the data from the data file&lt;br /&gt;
temp32=x32[:,0] #extracts the temperatures as a list from the data&lt;br /&gt;
E32=x32[:,1] #extracts the average energies from the data  &lt;br /&gt;
EE32=x32[:,2] #extracts the average square of the energies from the data &lt;br /&gt;
M32=x32[:,3] #extracts the average magnetisation from the data&lt;br /&gt;
MM32=x32[:,4] #extracts the average square of the magnetisations from the data &lt;br /&gt;
varE32=(np.array(EE32)-np.array(E32)**2)/(32*32) #calculates the variance of the energies per spin&lt;br /&gt;
varM32=(np.array(MM32)-np.array(M32)**2)/(32*32) #calculates the variance of the magnetisation per spin&lt;br /&gt;
Cv32=heat_capacity(temp32,varE32) #calculates the heat capacities&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,M32/(32*32),label=&#039;32x32&#039;) #plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;magnetisation per spin&#039;)&lt;br /&gt;
pl.title(&#039;how magnetisation per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;mag_vs_T.png&#039;) &lt;br /&gt;
pl.show()&lt;br /&gt;
&lt;br /&gt;
#plots the energy per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,E32/(32*32),label=&#039;32x32&#039;)&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;energy per spin&#039;)&lt;br /&gt;
pl.title(&#039;how energy per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;en_vs_T.png&#039;)&lt;br /&gt;
pl.show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As can be seen in figure 6a, the difference in the curve energy per spin vs temperature for 16x16 and 32x32 is small suggesting that they are both able to model the long range fluctuations. However there is still a slight in the magnetisation per spin vs temperature curve as seen in figure 6b, meaning that possibly the 16x16 lattice is not modelling them particularly well and a 32x32 lattice models them more accurately. If the lattice is to small the effect of one spin flipping is felt more greatly due to the periodic nature of the lattice if this is to small these spins can exert and effect on another. Both of these effects mean that the larger the lattice which is used the batter the prediction as it will model the long range fluctuation better and also eliminate the effect of the spin on its self as this decreases rapidly with distance.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
Figures 8,9 and 10 how how the data generated by our selves compares to the C++ data.&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|300px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
[[File:FkLvsTc.png|300px|thumb|figure 13: &amp;lt;math&amp;gt;\frac{1}{lattice length}\quad \mbox{vs} \quad T_{c,L} &amp;lt;/math&amp;gt;]]&lt;br /&gt;
As the Curie temperature changes with the size of the lattice according to equation 7.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{c,L}=\frac{A}{L}+T_{c,\infty}\quad \mbox{equation 7}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
The curie temperature of an infinite lattice can be predicted by finding the y intercept of the graph in figure 13. The Curie temperature of an infinite lattice is predicted to be 2.27681069 through the use of the graph. that predicted in literature is 2.16667 to the second order&amp;lt;ref&amp;gt;P. Li, Y. Song, Phys. Lett. A, 2001, &#039;&#039;&#039;289&#039;&#039;&#039;, 147-150.&amp;lt;/ref&amp;gt;. The value predicted by our model gives relatively good agreement. The major sources of error are likely to be due to the size of the lattices being looked at are small meaning that a by only flipping one spin there is a large effect on the average energy per spin. Also a grater number of montecarlo steps where used this would enable a better estimate of the energy to be calculated as the energy will be closer to the average energy for more of the steps.&lt;br /&gt;
==final Isinglattic code==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;creating the Ising lattice model&#039;&#039;&#039;&lt;br /&gt;
import numpy as np&lt;br /&gt;
&lt;br /&gt;
class IsingLattice:&lt;br /&gt;
# create statting values so as to keep runing totals of E, E^2, M, M^2 and n_cycles&lt;br /&gt;
    E = 0.0&lt;br /&gt;
    E2 = 0.0&lt;br /&gt;
    M = 0.0&lt;br /&gt;
    M2 = 0.0&lt;br /&gt;
&lt;br /&gt;
    n_cycles = 1&lt;br /&gt;
&lt;br /&gt;
    def __init__(self, n_rows, n_cols):&lt;br /&gt;
        self.n_rows = n_rows #number of rows in the lattise&lt;br /&gt;
        self.n_cols = n_cols #number of colums in the lattise&lt;br /&gt;
        self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols)) #generate random spins for each lattice site&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
        &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
    &lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        #returnes energy and magentisation of resulting state&lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
&lt;br /&gt;
    def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        #rerurnes E,E^2,M,M^2 and step number&lt;br /&gt;
        return E, E2, M, M2, n&lt;br /&gt;
        &lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Refrences==&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796631</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796631"/>
		<updated>2019-11-20T11:21:57Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* The effect of system size */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
&lt;br /&gt;
[[File:FkEn vs T.png|200px|thumb|figure 6,a:how energy per spin changes with temperature and lattice size]] &lt;br /&gt;
[[File:FkMag vs T.png|200px|thumb|figure 6,b:how magnetisation per spin changes with temperature and lattice size]] &lt;br /&gt;
these graphs are plotted using the following code&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
x32=np.loadtxt(&#039;32x32.dat&#039;) #extracts the data from the data file&lt;br /&gt;
temp32=x32[:,0] #extracts the temperatures as a list from the data&lt;br /&gt;
E32=x32[:,1] #extracts the average energies from the data  &lt;br /&gt;
EE32=x32[:,2] #extracts the average square of the energies from the data &lt;br /&gt;
M32=x32[:,3] #extracts the average magnetisation from the data&lt;br /&gt;
MM32=x32[:,4] #extracts the average square of the magnetisations from the data &lt;br /&gt;
varE32=(np.array(EE32)-np.array(E32)**2)/(32*32) #calculates the variance of the energies per spin&lt;br /&gt;
varM32=(np.array(MM32)-np.array(M32)**2)/(32*32) #calculates the variance of the magnetisation per spin&lt;br /&gt;
Cv32=heat_capacity(temp32,varE32) #calculates the heat capacities&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,M32/(32*32),label=&#039;32x32&#039;) #plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;magnetisation per spin&#039;)&lt;br /&gt;
pl.title(&#039;how magnetisation per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;mag_vs_T.png&#039;) &lt;br /&gt;
pl.show()&lt;br /&gt;
&lt;br /&gt;
#plots the energy per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,E32/(32*32),label=&#039;32x32&#039;)&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;energy per spin&#039;)&lt;br /&gt;
pl.title(&#039;how energy per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;en_vs_T.png&#039;)&lt;br /&gt;
pl.show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As can be seen in figure 6a, the difference in the curve energy per spin vs temperature for 16x16 and 32x32 is small suggesting that they are both able to model the long range fluctuations. However there is still a slight in the magnetisation per spin vs temperature curve as seen in figure 6b, meaning that possibly the 16x16 lattice is not modelling them particularly well and a 32x32 lattice models them more accurately. If the lattice is to small the effect of one spin flipping is felt more greatly due to the periodic nature of the lattice if this is to small these spins can exert and effect on another. Both of these effects mean that the larger the lattice which is used the batter the prediction as it will model the long range fluctuation better and also eliminate the effect of the spin on its self as this decreases rapidly with distance.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
Figures 8,9 and 10 how how the data generated by our selves compares to the C++ data.&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|300px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
[[File:FkLvsTc.png|300px|thumb|figure 13: &amp;lt;math&amp;gt;\frac{1}{lattice length}\quad \mbox{vs} \quad T_{c,L} &amp;lt;/math&amp;gt;]]&lt;br /&gt;
As the Curie temperature changes with the size of the lattice according to equation 7.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{c,L}=\frac{A}{L}+T_{c,\infty}\quad \mbox{equation 7}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
The curie temperature of an infinite lattice can be predicted by finding the y intercept of the graph in figure 13. The Curie temperature of an infinite lattice is predicted to be 2.27681069 through the use of the graph. that predicted in literature is 2.16667 to the second order&amp;lt;ref&amp;gt;P. Li, Y. Song, Phys. Lett. A, 2001, &#039;&#039;&#039;289&#039;&#039;&#039;, 147-150.&amp;lt;/ref&amp;gt;. The value predicted by our model gives relatively good agreement. The major sources of error are likely to be due to the size of the lattices being looked at are small meaning that a by only flipping one spin there is a large effect on the average energy per spin. Also a grater number of montecarlo steps where used this would enable a better estimate of the energy to be calculated as the energy will be closer to the average energy for more of the steps.&lt;br /&gt;
==final Isinglattic code==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;creating the Ising lattice model&#039;&#039;&#039;&lt;br /&gt;
import numpy as np&lt;br /&gt;
&lt;br /&gt;
class IsingLattice:&lt;br /&gt;
# create statting values so as to keep runing totals of E, E^2, M, M^2 and n_cycles&lt;br /&gt;
    E = 0.0&lt;br /&gt;
    E2 = 0.0&lt;br /&gt;
    M = 0.0&lt;br /&gt;
    M2 = 0.0&lt;br /&gt;
&lt;br /&gt;
    n_cycles = 1&lt;br /&gt;
&lt;br /&gt;
    def __init__(self, n_rows, n_cols):&lt;br /&gt;
        self.n_rows = n_rows #number of rows in the lattise&lt;br /&gt;
        self.n_cols = n_cols #number of colums in the lattise&lt;br /&gt;
        self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols)) #generate random spins for each lattice site&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
        &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
    &lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        #returnes energy and magentisation of resulting state&lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
&lt;br /&gt;
    def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        #rerurnes E,E^2,M,M^2 and step number&lt;br /&gt;
        return E, E2, M, M2, n&lt;br /&gt;
        &lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Refrences==&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796621</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796621"/>
		<updated>2019-11-20T11:08:29Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 16 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
&lt;br /&gt;
[[File:FkEn vs T.png|200px|thumb|figure 6,a:how energy per spin changes with temperature and lattice size]] &lt;br /&gt;
[[File:FkMag vs T.png|200px|thumb|figure 6,b:how magnetisation per spin changes with temperature and lattice size]] &lt;br /&gt;
these graphs are plotted using the following code&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
x32=np.loadtxt(&#039;32x32.dat&#039;) #extracts the data from the data file&lt;br /&gt;
temp32=x32[:,0] #extracts the temperatures as a list from the data&lt;br /&gt;
E32=x32[:,1] #extracts the average energies from the data  &lt;br /&gt;
EE32=x32[:,2] #extracts the average square of the energies from the data &lt;br /&gt;
M32=x32[:,3] #extracts the average magnetisation from the data&lt;br /&gt;
MM32=x32[:,4] #extracts the average square of the magnetisations from the data &lt;br /&gt;
varE32=(np.array(EE32)-np.array(E32)**2)/(32*32) #calculates the variance of the energies per spin&lt;br /&gt;
varM32=(np.array(MM32)-np.array(M32)**2)/(32*32) #calculates the variance of the magnetisation per spin&lt;br /&gt;
Cv32=heat_capacity(temp32,varE32) #calculates the heat capacities&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,M32/(32*32),label=&#039;32x32&#039;) #plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;magnetisation per spin&#039;)&lt;br /&gt;
pl.title(&#039;how magnetisation per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;mag_vs_T.png&#039;) &lt;br /&gt;
pl.show()&lt;br /&gt;
&lt;br /&gt;
#plots the energy per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,E32/(32*32),label=&#039;32x32&#039;)&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;energy per spin&#039;)&lt;br /&gt;
pl.title(&#039;how energy per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;en_vs_T.png&#039;)&lt;br /&gt;
pl.show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the effect of short range fluctuations decreases with lattice size as the spins are no longer as close to them selves as the lattice increases in size meaning that the profiles become more similar as seen in figures 6a,b. &lt;br /&gt;
&lt;br /&gt;
long range fluctuations can be captured in lattices which are 16x16 or grater.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
Figures 8,9 and 10 how how the data generated by our selves compares to the C++ data.&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|300px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
[[File:FkLvsTc.png|300px|thumb|figure 13: &amp;lt;math&amp;gt;\frac{1}{lattice length}\quad \mbox{vs} \quad T_{c,L} &amp;lt;/math&amp;gt;]]&lt;br /&gt;
As the Curie temperature changes with the size of the lattice according to equation 7.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{c,L}=\frac{A}{L}+T_{c,\infty}\quad \mbox{equation 7}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
The curie temperature of an infinite lattice can be predicted by finding the y intercept of the graph in figure 13. The Curie temperature of an infinite lattice is predicted to be 2.27681069 through the use of the graph. that predicted in literature is 2.16667 to the second order&amp;lt;ref&amp;gt;P. Li, Y. Song, Phys. Lett. A, 2001, &#039;&#039;&#039;289&#039;&#039;&#039;, 147-150.&amp;lt;/ref&amp;gt;. The value predicted by our model gives relatively good agreement. The major sources of error are likely to be due to the size of the lattices being looked at are small meaning that a by only flipping one spin there is a large effect on the average energy per spin. Also a grater number of montecarlo steps where used this would enable a better estimate of the energy to be calculated as the energy will be closer to the average energy for more of the steps.&lt;br /&gt;
==final Isinglattic code==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;creating the Ising lattice model&#039;&#039;&#039;&lt;br /&gt;
import numpy as np&lt;br /&gt;
&lt;br /&gt;
class IsingLattice:&lt;br /&gt;
# create statting values so as to keep runing totals of E, E^2, M, M^2 and n_cycles&lt;br /&gt;
    E = 0.0&lt;br /&gt;
    E2 = 0.0&lt;br /&gt;
    M = 0.0&lt;br /&gt;
    M2 = 0.0&lt;br /&gt;
&lt;br /&gt;
    n_cycles = 1&lt;br /&gt;
&lt;br /&gt;
    def __init__(self, n_rows, n_cols):&lt;br /&gt;
        self.n_rows = n_rows #number of rows in the lattise&lt;br /&gt;
        self.n_cols = n_cols #number of colums in the lattise&lt;br /&gt;
        self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols)) #generate random spins for each lattice site&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
        &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
    &lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        #returnes energy and magentisation of resulting state&lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
&lt;br /&gt;
    def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        #rerurnes E,E^2,M,M^2 and step number&lt;br /&gt;
        return E, E2, M, M2, n&lt;br /&gt;
        &lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Refrences==&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796397</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796397"/>
		<updated>2019-11-20T03:40:49Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 19 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
&lt;br /&gt;
[[File:FkEn vs T.png|200px|thumb|figure 6,a:how energy per spin changes with temperature and lattice size]] &lt;br /&gt;
[[File:FkMag vs T.png|200px|thumb|figure 6,b:how magnetisation per spin changes with temperature and lattice size]] &lt;br /&gt;
these graphs are plotted using the following code&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
x32=np.loadtxt(&#039;32x32.dat&#039;) #extracts the data from the data file&lt;br /&gt;
temp32=x32[:,0] #extracts the temperatures as a list from the data&lt;br /&gt;
E32=x32[:,1] #extracts the average energies from the data  &lt;br /&gt;
EE32=x32[:,2] #extracts the average square of the energies from the data &lt;br /&gt;
M32=x32[:,3] #extracts the average magnetisation from the data&lt;br /&gt;
MM32=x32[:,4] #extracts the average square of the magnetisations from the data &lt;br /&gt;
varE32=(np.array(EE32)-np.array(E32)**2)/(32*32) #calculates the variance of the energies per spin&lt;br /&gt;
varM32=(np.array(MM32)-np.array(M32)**2)/(32*32) #calculates the variance of the magnetisation per spin&lt;br /&gt;
Cv32=heat_capacity(temp32,varE32) #calculates the heat capacities&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,M32/(32*32),label=&#039;32x32&#039;) #plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;magnetisation per spin&#039;)&lt;br /&gt;
pl.title(&#039;how magnetisation per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;mag_vs_T.png&#039;) &lt;br /&gt;
pl.show()&lt;br /&gt;
&lt;br /&gt;
#plots the energy per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,E32/(32*32),label=&#039;32x32&#039;)&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;energy per spin&#039;)&lt;br /&gt;
pl.title(&#039;how energy per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;en_vs_T.png&#039;)&lt;br /&gt;
pl.show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the effect of short range fluctuations decreases with lattice size as the spins are no longer as close to them selves as the lattice increases in size meaning that the profiles become more similar as seen in figures 6a,b. &lt;br /&gt;
&lt;br /&gt;
long range fluctuations can be captured in lattices which are 16x16 or grater.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|300px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
[[File:FkLvsTc.png|300px|thumb|figure 13: &amp;lt;math&amp;gt;\frac{1}{lattice length}\quad \mbox{vs} \quad T_{c,L} &amp;lt;/math&amp;gt;]]&lt;br /&gt;
As the Curie temperature changes with the size of the lattice according to equation 7.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{c,L}=\frac{A}{L}+T_{c,\infty}\quad \mbox{equation 7}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
The curie temperature of an infinite lattice can be predicted by finding the y intercept of the graph in figure 13. The Curie temperature of an infinite lattice is predicted to be 2.27681069 through the use of the graph. that predicted in literature is 2.16667 to the second order&amp;lt;ref&amp;gt;P. Li, Y. Song, Phys. Lett. A, 2001, &#039;&#039;&#039;289&#039;&#039;&#039;, 147-150.&amp;lt;/ref&amp;gt;. The value predicted by our model gives relatively good agreement. The major sources of error are likely to be due to the size of the lattices being looked at are small meaning that a by only flipping one spin there is a large effect on the average energy per spin. Also a grater number of montecarlo steps where used this would enable a better estimate of the energy to be calculated as the energy will be closer to the average energy for more of the steps.&lt;br /&gt;
==final Isinglattic code==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;creating the Ising lattice model&#039;&#039;&#039;&lt;br /&gt;
import numpy as np&lt;br /&gt;
&lt;br /&gt;
class IsingLattice:&lt;br /&gt;
# create statting values so as to keep runing totals of E, E^2, M, M^2 and n_cycles&lt;br /&gt;
    E = 0.0&lt;br /&gt;
    E2 = 0.0&lt;br /&gt;
    M = 0.0&lt;br /&gt;
    M2 = 0.0&lt;br /&gt;
&lt;br /&gt;
    n_cycles = 1&lt;br /&gt;
&lt;br /&gt;
    def __init__(self, n_rows, n_cols):&lt;br /&gt;
        self.n_rows = n_rows #number of rows in the lattise&lt;br /&gt;
        self.n_cols = n_cols #number of colums in the lattise&lt;br /&gt;
        self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols)) #generate random spins for each lattice site&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
        &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
    &lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        #returnes energy and magentisation of resulting state&lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
&lt;br /&gt;
    def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        #rerurnes E,E^2,M,M^2 and step number&lt;br /&gt;
        return E, E2, M, M2, n&lt;br /&gt;
        &lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Refrences==&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796396</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796396"/>
		<updated>2019-11-20T03:28:34Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 13 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
&lt;br /&gt;
[[File:FkEn vs T.png|200px|thumb|figure 6,a:how energy per spin changes with temperature and lattice size]] &lt;br /&gt;
[[File:FkMag vs T.png|200px|thumb|figure 6,b:how magnetisation per spin changes with temperature and lattice size]] &lt;br /&gt;
these graphs are plotted using the following code&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
x32=np.loadtxt(&#039;32x32.dat&#039;) #extracts the data from the data file&lt;br /&gt;
temp32=x32[:,0] #extracts the temperatures as a list from the data&lt;br /&gt;
E32=x32[:,1] #extracts the average energies from the data  &lt;br /&gt;
EE32=x32[:,2] #extracts the average square of the energies from the data &lt;br /&gt;
M32=x32[:,3] #extracts the average magnetisation from the data&lt;br /&gt;
MM32=x32[:,4] #extracts the average square of the magnetisations from the data &lt;br /&gt;
varE32=(np.array(EE32)-np.array(E32)**2)/(32*32) #calculates the variance of the energies per spin&lt;br /&gt;
varM32=(np.array(MM32)-np.array(M32)**2)/(32*32) #calculates the variance of the magnetisation per spin&lt;br /&gt;
Cv32=heat_capacity(temp32,varE32) #calculates the heat capacities&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,M32/(32*32),label=&#039;32x32&#039;) #plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;magnetisation per spin&#039;)&lt;br /&gt;
pl.title(&#039;how magnetisation per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;mag_vs_T.png&#039;) &lt;br /&gt;
pl.show()&lt;br /&gt;
&lt;br /&gt;
#plots the energy per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,E32/(32*32),label=&#039;32x32&#039;)&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;energy per spin&#039;)&lt;br /&gt;
pl.title(&#039;how energy per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;en_vs_T.png&#039;)&lt;br /&gt;
pl.show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the effect of short range fluctuations decreases with lattice size as the spins are no longer as close to them selves as the lattice increases in size meaning that the profiles become more similar as seen in figures 6a,b. &lt;br /&gt;
&lt;br /&gt;
long range fluctuations can be captured in lattices which are 16x16 or grater.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|300px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
[[File:FkLvsTc.png|300px|thumb|figure 13: &amp;lt;math&amp;gt;\frac{1}{lattice length}\quad \mbox{vs} \quad T_{c,L} &amp;lt;/math&amp;gt;]]&lt;br /&gt;
As the Curie temperature changes with the size of the lattice according to equation 7.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{c,L}=\frac{A}{L}+T_{c,\infty}\quad \mbox{equation 7}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
The curie temperature of an infinite lattice can be predicted by finding the y intercept of the graph in figure 13. The Curie temperature of an infinite lattice is predicted to be 2.27681069 through the use of the graph. that predicted in literature is 2.16667 to the second order&amp;lt;ref&amp;gt;P. Li, Y. Song, Phys. Lett. A, 2001, &#039;&#039;&#039;289&#039;&#039;&#039;, 147-150.&amp;lt;/ref&amp;gt;. The value predicted by our model gives relatively good agreement. The major sources of error are likely to be due to the size of the lattices being looked at are small meaning that a by only flipping one spin there is a large effect on the average energy per spin. Also a grater number of montecarlo steps where used this would enable a better estimate of the energy to be calculated as the energy will be closer to the average energy for more of the steps.&lt;br /&gt;
&lt;br /&gt;
==Refrences==&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796395</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796395"/>
		<updated>2019-11-20T03:22:43Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Refrences */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
&lt;br /&gt;
[[File:FkEn vs T.png|200px|thumb|figure 6,a:how energy per spin changes with temperature and lattice size]] &lt;br /&gt;
[[File:FkMag vs T.png|200px|thumb|figure 6,b:how magnetisation per spin changes with temperature and lattice size]] &lt;br /&gt;
these graphs are plotted using the following code&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
x32=np.loadtxt(&#039;32x32.dat&#039;) #extracts the data from the data file&lt;br /&gt;
temp32=x32[:,0] #extracts the temperatures as a list from the data&lt;br /&gt;
E32=x32[:,1] #extracts the average energies from the data  &lt;br /&gt;
EE32=x32[:,2] #extracts the average square of the energies from the data &lt;br /&gt;
M32=x32[:,3] #extracts the average magnetisation from the data&lt;br /&gt;
MM32=x32[:,4] #extracts the average square of the magnetisations from the data &lt;br /&gt;
varE32=(np.array(EE32)-np.array(E32)**2)/(32*32) #calculates the variance of the energies per spin&lt;br /&gt;
varM32=(np.array(MM32)-np.array(M32)**2)/(32*32) #calculates the variance of the magnetisation per spin&lt;br /&gt;
Cv32=heat_capacity(temp32,varE32) #calculates the heat capacities&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,M32/(32*32),label=&#039;32x32&#039;) #plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;magnetisation per spin&#039;)&lt;br /&gt;
pl.title(&#039;how magnetisation per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;mag_vs_T.png&#039;) &lt;br /&gt;
pl.show()&lt;br /&gt;
&lt;br /&gt;
#plots the energy per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,E32/(32*32),label=&#039;32x32&#039;)&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;energy per spin&#039;)&lt;br /&gt;
pl.title(&#039;how energy per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;en_vs_T.png&#039;)&lt;br /&gt;
pl.show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the effect of short range fluctuations decreases with lattice size as the spins are no longer as close to them selves as the lattice increases in size meaning that the profiles become more similar as seen in figures 6a,b. &lt;br /&gt;
&lt;br /&gt;
long range fluctuations&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|300px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
[[File:FkLvsTc.png|300px|thumb|figure 13: &amp;lt;math&amp;gt;\frac{1}{lattice length}\quad \mbox{vs} \quad T_{c,L} &amp;lt;/math&amp;gt;]]&lt;br /&gt;
As the Curie temperature changes with the size of the lattice according to equation 7.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{c,L}=\frac{A}{L}+T_{c,\infty}\quad \mbox{equation 7}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
The curie temperature of an infinite lattice can be predicted by finding the y intercept of the graph in figure 13. The Curie temperature of an infinite lattice is predicted to be 2.27681069 through the use of the graph. that predicted in literature is 2.16667 to the second order&amp;lt;ref&amp;gt;P. Li, Y. Song, Phys. Lett. A, 2001, &#039;&#039;&#039;289&#039;&#039;&#039;, 147-150.&amp;lt;/ref&amp;gt;. The value predicted by our model gives relatively good agreement. The major sources of error are likely to be due to the size of the lattices being looked at are small meaning that a by only flipping one spin there is a large effect on the average energy per spin. Also a grater number of montecarlo steps where used this would enable a better estimate of the energy to be calculated as the energy will be closer to the average energy for more of the steps.&lt;br /&gt;
&lt;br /&gt;
==Refrences==&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796394</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796394"/>
		<updated>2019-11-20T03:21:25Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Refrences */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
&lt;br /&gt;
[[File:FkEn vs T.png|200px|thumb|figure 6,a:how energy per spin changes with temperature and lattice size]] &lt;br /&gt;
[[File:FkMag vs T.png|200px|thumb|figure 6,b:how magnetisation per spin changes with temperature and lattice size]] &lt;br /&gt;
these graphs are plotted using the following code&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
x32=np.loadtxt(&#039;32x32.dat&#039;) #extracts the data from the data file&lt;br /&gt;
temp32=x32[:,0] #extracts the temperatures as a list from the data&lt;br /&gt;
E32=x32[:,1] #extracts the average energies from the data  &lt;br /&gt;
EE32=x32[:,2] #extracts the average square of the energies from the data &lt;br /&gt;
M32=x32[:,3] #extracts the average magnetisation from the data&lt;br /&gt;
MM32=x32[:,4] #extracts the average square of the magnetisations from the data &lt;br /&gt;
varE32=(np.array(EE32)-np.array(E32)**2)/(32*32) #calculates the variance of the energies per spin&lt;br /&gt;
varM32=(np.array(MM32)-np.array(M32)**2)/(32*32) #calculates the variance of the magnetisation per spin&lt;br /&gt;
Cv32=heat_capacity(temp32,varE32) #calculates the heat capacities&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,M32/(32*32),label=&#039;32x32&#039;) #plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;magnetisation per spin&#039;)&lt;br /&gt;
pl.title(&#039;how magnetisation per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;mag_vs_T.png&#039;) &lt;br /&gt;
pl.show()&lt;br /&gt;
&lt;br /&gt;
#plots the energy per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,E32/(32*32),label=&#039;32x32&#039;)&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;energy per spin&#039;)&lt;br /&gt;
pl.title(&#039;how energy per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;en_vs_T.png&#039;)&lt;br /&gt;
pl.show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the effect of short range fluctuations decreases with lattice size as the spins are no longer as close to them selves as the lattice increases in size meaning that the profiles become more similar as seen in figures 6a,b. &lt;br /&gt;
&lt;br /&gt;
long range fluctuations&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|300px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
[[File:FkLvsTc.png|300px|thumb|figure 13: &amp;lt;math&amp;gt;\frac{1}{lattice length}\quad \mbox{vs} \quad T_{c,L} &amp;lt;/math&amp;gt;]]&lt;br /&gt;
As the Curie temperature changes with the size of the lattice according to equation 7.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{c,L}=\frac{A}{L}+T_{c,\infty}\quad \mbox{equation 7}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
The curie temperature of an infinite lattice can be predicted by finding the y intercept of the graph in figure 13. The Curie temperature of an infinite lattice is predicted to be 2.27681069 through the use of the graph. that predicted in literature is 2.16667 to the second order&amp;lt;ref&amp;gt;P. Li, Y. Song, Phys. Lett. A, 2001, 289, 147-150.&amp;lt;/ref&amp;gt;. The value predicted by our model gives relatively good agreement. The major sources of error are likely to be due to the size of the lattices being looked at are small meaning that a by only flipping one spin there is a large effect on the average energy per spin. Also a grater number of montecarlo steps where used this would enable a better estimate of the energy to be calculated as the energy will be closer to the average energy for more of the steps.&lt;br /&gt;
&lt;br /&gt;
==Refrences==&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796393</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796393"/>
		<updated>2019-11-20T03:21:00Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 19 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
&lt;br /&gt;
[[File:FkEn vs T.png|200px|thumb|figure 6,a:how energy per spin changes with temperature and lattice size]] &lt;br /&gt;
[[File:FkMag vs T.png|200px|thumb|figure 6,b:how magnetisation per spin changes with temperature and lattice size]] &lt;br /&gt;
these graphs are plotted using the following code&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
x32=np.loadtxt(&#039;32x32.dat&#039;) #extracts the data from the data file&lt;br /&gt;
temp32=x32[:,0] #extracts the temperatures as a list from the data&lt;br /&gt;
E32=x32[:,1] #extracts the average energies from the data  &lt;br /&gt;
EE32=x32[:,2] #extracts the average square of the energies from the data &lt;br /&gt;
M32=x32[:,3] #extracts the average magnetisation from the data&lt;br /&gt;
MM32=x32[:,4] #extracts the average square of the magnetisations from the data &lt;br /&gt;
varE32=(np.array(EE32)-np.array(E32)**2)/(32*32) #calculates the variance of the energies per spin&lt;br /&gt;
varM32=(np.array(MM32)-np.array(M32)**2)/(32*32) #calculates the variance of the magnetisation per spin&lt;br /&gt;
Cv32=heat_capacity(temp32,varE32) #calculates the heat capacities&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,M32/(32*32),label=&#039;32x32&#039;) #plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;magnetisation per spin&#039;)&lt;br /&gt;
pl.title(&#039;how magnetisation per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;mag_vs_T.png&#039;) &lt;br /&gt;
pl.show()&lt;br /&gt;
&lt;br /&gt;
#plots the energy per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,E32/(32*32),label=&#039;32x32&#039;)&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;energy per spin&#039;)&lt;br /&gt;
pl.title(&#039;how energy per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;en_vs_T.png&#039;)&lt;br /&gt;
pl.show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the effect of short range fluctuations decreases with lattice size as the spins are no longer as close to them selves as the lattice increases in size meaning that the profiles become more similar as seen in figures 6a,b. &lt;br /&gt;
&lt;br /&gt;
long range fluctuations&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|300px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
[[File:FkLvsTc.png|300px|thumb|figure 13: &amp;lt;math&amp;gt;\frac{1}{lattice length}\quad \mbox{vs} \quad T_{c,L} &amp;lt;/math&amp;gt;]]&lt;br /&gt;
As the Curie temperature changes with the size of the lattice according to equation 7.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{c,L}=\frac{A}{L}+T_{c,\infty}\quad \mbox{equation 7}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
The curie temperature of an infinite lattice can be predicted by finding the y intercept of the graph in figure 13. The Curie temperature of an infinite lattice is predicted to be 2.27681069 through the use of the graph. that predicted in literature is 2.16667 to the second order&amp;lt;ref&amp;gt;P. Li, Y. Song, Phys. Lett. A, 2001, 289, 147-150.&amp;lt;/ref&amp;gt;. The value predicted by our model gives relatively good agreement. The major sources of error are likely to be due to the size of the lattices being looked at are small meaning that a by only flipping one spin there is a large effect on the average energy per spin. Also a grater number of montecarlo steps where used this would enable a better estimate of the energy to be calculated as the energy will be closer to the average energy for more of the steps.&lt;br /&gt;
&lt;br /&gt;
==Refrences==&lt;br /&gt;
{{Reflist}}&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796391</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796391"/>
		<updated>2019-11-20T03:12:03Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 19 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
&lt;br /&gt;
[[File:FkEn vs T.png|200px|thumb|figure 6,a:how energy per spin changes with temperature and lattice size]] &lt;br /&gt;
[[File:FkMag vs T.png|200px|thumb|figure 6,b:how magnetisation per spin changes with temperature and lattice size]] &lt;br /&gt;
these graphs are plotted using the following code&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
x32=np.loadtxt(&#039;32x32.dat&#039;) #extracts the data from the data file&lt;br /&gt;
temp32=x32[:,0] #extracts the temperatures as a list from the data&lt;br /&gt;
E32=x32[:,1] #extracts the average energies from the data  &lt;br /&gt;
EE32=x32[:,2] #extracts the average square of the energies from the data &lt;br /&gt;
M32=x32[:,3] #extracts the average magnetisation from the data&lt;br /&gt;
MM32=x32[:,4] #extracts the average square of the magnetisations from the data &lt;br /&gt;
varE32=(np.array(EE32)-np.array(E32)**2)/(32*32) #calculates the variance of the energies per spin&lt;br /&gt;
varM32=(np.array(MM32)-np.array(M32)**2)/(32*32) #calculates the variance of the magnetisation per spin&lt;br /&gt;
Cv32=heat_capacity(temp32,varE32) #calculates the heat capacities&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,M32/(32*32),label=&#039;32x32&#039;) #plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;magnetisation per spin&#039;)&lt;br /&gt;
pl.title(&#039;how magnetisation per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;mag_vs_T.png&#039;) &lt;br /&gt;
pl.show()&lt;br /&gt;
&lt;br /&gt;
#plots the energy per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,E32/(32*32),label=&#039;32x32&#039;)&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;energy per spin&#039;)&lt;br /&gt;
pl.title(&#039;how energy per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;en_vs_T.png&#039;)&lt;br /&gt;
pl.show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the effect of short range fluctuations decreases with lattice size as the spins are no longer as close to them selves as the lattice increases in size meaning that the profiles become more similar as seen in figures 6a,b. &lt;br /&gt;
&lt;br /&gt;
long range fluctuations&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|300px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
[[File:FkLvsTc.png|300px|thumb|figure 13: &amp;lt;math&amp;gt;\frac{1}{lattice length}\quad \mbox{vs} \quad T_{c,L} &amp;lt;/math&amp;gt;]]&lt;br /&gt;
As the Curie temperature changes with the size of the lattice according to equation 7.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{c,L}=\frac{A}{L}+T_{c,\infty}\quad \mbox{equation 7}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
The curie temperature of an infinite lattice can be predicted by finding the y intercept of the graph in figure 13. The Curie temperature of an infinite lattice is predicted to be 2.27681069 through the use of the graph. that predicted in literature is 2.16667 to the second order&amp;lt;ref&amp;gt;1&amp;lt;/ref&amp;gt;. The value predicted by our model gives relatively good agreement. The major sources of error are likely to be due to the size of the lattices being looked at are small meaning that a by only flipping one spin there is a large effect on the average energy per spin. Also a grater number of montecarlo steps where used this would enable a better estimate of the energy to be calculated as the energy will be closer to the average energy for more of the steps.&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:FkLvsTc.png&amp;diff=796388</id>
		<title>File:FkLvsTc.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:FkLvsTc.png&amp;diff=796388"/>
		<updated>2019-11-20T02:54:55Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: Fgk17 uploaded a new version of File:FkLvsTc.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796387</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796387"/>
		<updated>2019-11-20T02:52:23Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 13 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
&lt;br /&gt;
[[File:FkEn vs T.png|200px|thumb|figure 6,a:how energy per spin changes with temperature and lattice size]] &lt;br /&gt;
[[File:FkMag vs T.png|200px|thumb|figure 6,b:how magnetisation per spin changes with temperature and lattice size]] &lt;br /&gt;
these graphs are plotted using the following code&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
x32=np.loadtxt(&#039;32x32.dat&#039;) #extracts the data from the data file&lt;br /&gt;
temp32=x32[:,0] #extracts the temperatures as a list from the data&lt;br /&gt;
E32=x32[:,1] #extracts the average energies from the data  &lt;br /&gt;
EE32=x32[:,2] #extracts the average square of the energies from the data &lt;br /&gt;
M32=x32[:,3] #extracts the average magnetisation from the data&lt;br /&gt;
MM32=x32[:,4] #extracts the average square of the magnetisations from the data &lt;br /&gt;
varE32=(np.array(EE32)-np.array(E32)**2)/(32*32) #calculates the variance of the energies per spin&lt;br /&gt;
varM32=(np.array(MM32)-np.array(M32)**2)/(32*32) #calculates the variance of the magnetisation per spin&lt;br /&gt;
Cv32=heat_capacity(temp32,varE32) #calculates the heat capacities&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,M32/(32*32),label=&#039;32x32&#039;) #plots the magnetisation per spin verses the temperature&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;magnetisation per spin&#039;)&lt;br /&gt;
pl.title(&#039;how magnetisation per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;mag_vs_T.png&#039;) &lt;br /&gt;
pl.show()&lt;br /&gt;
&lt;br /&gt;
#plots the energy per spin verses the temperature&lt;br /&gt;
pl.plot(temp32,E32/(32*32),label=&#039;32x32&#039;)&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;energy per spin&#039;)&lt;br /&gt;
pl.title(&#039;how energy per spin varies as temperature increases for diffrent lattices sizes&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;en_vs_T.png&#039;)&lt;br /&gt;
pl.show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the effect of short range fluctuations decreases with lattice size as the spins are no longer as close to them selves as the lattice increases in size meaning that the profiles become more similar as seen in figures 6a,b. &lt;br /&gt;
&lt;br /&gt;
long range fluctuations&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|300px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
[[File:FkLvsTc.png|300px|thumb|figure 13: &amp;lt;math&amp;gt;\frac{1}{lattice length}\quad \mbox{vs} \quad T_{c,L} &amp;lt;/math&amp;gt;]]&lt;br /&gt;
As the Curie temperature changes with the size of the lattice according to equation 7.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{c,L}=\frac{A}{L}+T_{c,\infty}\quad \mbox{equation 7}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
The curie temperature of an infinite lattice can be predicted by finding the y intercept of the graph in figure 13. The Curie temperature of an infinite lattice is predicted to be 2.27681069 through the use of the graph. that predicted in literature is   &lt;br /&gt;
&lt;br /&gt;
=====find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of  for that side length. Make a plot that uses the scaling relation given above to determine . By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate. ======&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:FkEn_vs_T.png&amp;diff=796382</id>
		<title>File:FkEn vs T.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:FkEn_vs_T.png&amp;diff=796382"/>
		<updated>2019-11-20T02:39:13Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:FkMag_vs_T.png&amp;diff=796380</id>
		<title>File:FkMag vs T.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:FkMag_vs_T.png&amp;diff=796380"/>
		<updated>2019-11-20T02:37:12Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796375</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796375"/>
		<updated>2019-11-20T02:28:45Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 19 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
=====Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?=====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
the long range fluctuations are &lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|300px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
[[File:FkLvsTc.png|300px|thumb|figure 13: &amp;lt;math&amp;gt;\frac{1}{lattice length}\quad \mbox{vs} \quad T_{c,L} &amp;lt;/math&amp;gt;]]&lt;br /&gt;
As the Curie temperature changes with the size of the lattice according to equation 7.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{c,L}=\frac{A}{L}+T_{c,\infty}\quad \mbox{equation 7}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
The curie temperature of an infinite lattice can be predicted by finding the y intercept of the graph in figure 13. The Curie temperature of an infinite lattice is predicted to be 2.27681069 through the use of the graph. that predicted in literature is   &lt;br /&gt;
&lt;br /&gt;
=====find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of  for that side length. Make a plot that uses the scaling relation given above to determine . By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate. ======&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796373</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796373"/>
		<updated>2019-11-20T02:17:31Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 19 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
=====Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?=====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
the long range fluctuations are &lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|300px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
&amp;lt;math&amp;gt;T_{c,L}=\frac{A}{L}+T_{c,\infty}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
[[File:FkLvsTc.png|300px|thumb|figure 13: &amp;lt;math&amp;gt;\frac{1}{lattice length}\quad \mbox{vs} \quad T_{c,L} &amp;lt;/math&amp;gt;]]&lt;br /&gt;
=====find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of  for that side length. Make a plot that uses the scaling relation given above to determine . By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate. ======&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796372</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796372"/>
		<updated>2019-11-20T02:16:50Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 19 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
=====Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?=====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
the long range fluctuations are &lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|300px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
&amp;lt;math&amp;gt;T_{c,L}=\frac{A}{L}+T_{c,\infty}&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
[[File:FkLvsTc.png|300px|thumb|figure 13: &amp;lt;math&amp;gt;\frac{1}{lattice length}\quad \mbox{vs} \quad T_c&amp;lt;/math&amp;gt;&lt;br /&gt;
=====find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of  for that side length. Make a plot that uses the scaling relation given above to determine . By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate. ======&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:FkLvsTc.png&amp;diff=796369</id>
		<title>File:FkLvsTc.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:FkLvsTc.png&amp;diff=796369"/>
		<updated>2019-11-20T02:12:47Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796364</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796364"/>
		<updated>2019-11-20T01:59:05Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 19 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
=====Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?=====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
the long range fluctuations are &lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|300px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
&amp;lt;math&amp;gt;T_{c,L}=\frac{A}{L}+T_{c,\infity}&amp;lt;/math&amp;gt; &lt;br /&gt;
=====find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of  for that side length. Make a plot that uses the scaling relation given above to determine . By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate. ======&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796343</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796343"/>
		<updated>2019-11-20T01:11:38Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 17 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
=====Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?=====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
the long range fluctuations are &lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|300px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
=====find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of  for that side length. Make a plot that uses the scaling relation given above to determine . By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate. ======&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796341</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796341"/>
		<updated>2019-11-20T01:11:12Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 16 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
=====Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?=====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
the long range fluctuations are &lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|300px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|300px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|300px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|200px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
=====find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of  for that side length. Make a plot that uses the scaling relation given above to determine . By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate. ======&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796339</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796339"/>
		<updated>2019-11-20T01:10:49Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 16 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
=====Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?=====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
the long range fluctuations are &lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|250px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|250px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|250px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|200px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
=====find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of  for that side length. Make a plot that uses the scaling relation given above to determine . By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate. ======&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796337</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796337"/>
		<updated>2019-11-20T01:10:21Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 15 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
=====Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?=====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
the long range fluctuations are &lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|200px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|200px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|200px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|200px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
=====find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of  for that side length. Make a plot that uses the scaling relation given above to determine . By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate. ======&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796335</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796335"/>
		<updated>2019-11-20T01:04:37Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region. */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
=====Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?=====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
the long range fluctuations are &lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|200px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|200px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|200px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|200px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
&lt;br /&gt;
[[File:Fk polyfit 32 2st.png|200px|thumb|left|figure 12: C vs T for a 32x32 lattice where the peak is fitted]]&lt;br /&gt;
Figure 12 is generated using the code bellow where only the peak has been fitted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
Tmin=2.0 #temperature around the peak&lt;br /&gt;
Tmax=2.75 #temperature around the peak&lt;br /&gt;
&lt;br /&gt;
selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #select only the data around the peak of the curve&lt;br /&gt;
peak_T_values = T[selection]&lt;br /&gt;
peak_C_values = C[selection]&lt;br /&gt;
&lt;br /&gt;
#data is fitted&lt;br /&gt;
fit2 = np.polyfit(peak_T_values, peak_C_values,9)#fit to the 10th order&lt;br /&gt;
T_min2 = np.min(peak_T_values)&lt;br /&gt;
T_max2 = np.max(peak_T_values)&lt;br /&gt;
T_range2 = np.linspace(T_min2, T_max2, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values2 = np.polyval(fit2, T_range2)&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;C++ data 32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range2,fitted_C_values2,label=&#039;fitted curve&#039;) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.legend()&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_2st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
=====find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of  for that side length. Make a plot that uses the scaling relation given above to determine . By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate. ======&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:Fk_polyfit_32_2st.png&amp;diff=796332</id>
		<title>File:Fk polyfit 32 2st.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:Fk_polyfit_32_2st.png&amp;diff=796332"/>
		<updated>2019-11-20T01:00:36Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796323</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796323"/>
		<updated>2019-11-20T00:40:36Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 17 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
=====Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?=====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
the long range fluctuations are &lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|200px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|200px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|200px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
An initial fit for the C vs T data is generated by the code below so as to give figure 11 for a 32x32 lattice&lt;br /&gt;
[[File:Fk polyfit 32 1st.png|200px|thumb|figure 11: C vs T plot and fitted curve for 32x32 lattice]]&lt;br /&gt;
&amp;lt;pre&amp;gt;data = np.loadtxt(&#039;C32x32.dat&#039;)#C++ 32x32 generated data file is called&lt;br /&gt;
T = data[:,0] #get the first column&lt;br /&gt;
C = data[:,5] # get the sixth column&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#a the polynomial is fitted to the data&lt;br /&gt;
fit = np.polyfit(T, C, 16) # fit a 16th order polynomial&lt;br /&gt;
&lt;br /&gt;
#the maximum and minimum temperatures are found &lt;br /&gt;
T_min = np.min(T)&lt;br /&gt;
T_max = np.max(T)&lt;br /&gt;
T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C_values = np.polyval(fit, T_range) # use the fit object to generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
#data is plotted &lt;br /&gt;
pl.plot(T,C,label=&#039;32x32&#039;) #data is plotted&lt;br /&gt;
pl.plot(T_range,fitted_C_values) #curve is plotted&lt;br /&gt;
pl.xlabel(&#039;temperature&#039;)&lt;br /&gt;
pl.ylabel(&#039;heat capacity&#039;)&lt;br /&gt;
pl.savefig(&#039;fk_polyfit_32_1st.png&#039;)&lt;br /&gt;
pl.show()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
=====Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region. =====&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
=====find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of  for that side length. Make a plot that uses the scaling relation given above to determine . By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate. ======&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:Fk_polyfit_32_1st.png&amp;diff=796322</id>
		<title>File:Fk polyfit 32 1st.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:Fk_polyfit_32_1st.png&amp;diff=796322"/>
		<updated>2019-11-20T00:39:07Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796313</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796313"/>
		<updated>2019-11-20T00:22:08Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 16 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
=====Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?=====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
the long range fluctuations are &lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|200px|thumb|right|figure 10: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|200px|thumb|left|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|200px|thumb|centre|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
=====write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report. =====&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
=====Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region. =====&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
=====find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of  for that side length. Make a plot that uses the scaling relation given above to determine . By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate. ======&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796312</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796312"/>
		<updated>2019-11-20T00:21:13Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 16 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
=====Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?=====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
the long range fluctuations are &lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
[[File:Fk C vs self Cv.png|200px|thumb|figure 7: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self E.png|200px|thumb|figure 8: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
[[File:Fk C vs self M.png|200px|thumb|figure 9: comparisons between C++ data and self generated data for an 8x8 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
=====write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report. =====&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
=====Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region. =====&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
=====find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of  for that side length. Make a plot that uses the scaling relation given above to determine . By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate. ======&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:Fk_C_vs_self_M.png&amp;diff=796305</id>
		<title>File:Fk C vs self M.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:Fk_C_vs_self_M.png&amp;diff=796305"/>
		<updated>2019-11-20T00:16:54Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:Fk_C_vs_self_E.png&amp;diff=796304</id>
		<title>File:Fk C vs self E.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:Fk_C_vs_self_E.png&amp;diff=796304"/>
		<updated>2019-11-20T00:16:22Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:Fk_C_vs_self_Cv.png&amp;diff=796303</id>
		<title>File:Fk C vs self Cv.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:Fk_C_vs_self_Cv.png&amp;diff=796303"/>
		<updated>2019-11-20T00:14:33Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796287</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796287"/>
		<updated>2019-11-19T23:43:38Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 15 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
=====Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?=====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
the long range fluctuations are &lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
The following function is used to generate the values for the heat capacity&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def heat_capacity(T,var):&lt;br /&gt;
    &amp;quot;heat capacity form temperature an variance in units of k_B as E is already in unities of k_B&amp;quot;&lt;br /&gt;
    C=var/(T**2) &lt;br /&gt;
    return C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This was then plotted using the plot function to give figure 7.&lt;br /&gt;
[[File:Fk T Vs Cv.png||thumb|right|figure 7: plot of heat capacity against temperature for different lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
=====A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns:  (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object=====&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
=====write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report. =====&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
=====Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region. =====&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
=====find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of  for that side length. Make a plot that uses the scaling relation given above to determine . By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate. ======&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:Fk_T_Vs_Cv.png&amp;diff=796283</id>
		<title>File:Fk T Vs Cv.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:Fk_T_Vs_Cv.png&amp;diff=796283"/>
		<updated>2019-11-19T23:39:40Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796266</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796266"/>
		<updated>2019-11-19T23:12:40Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* The effect of temperature */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0, however change rapidly within the first 20000 steps and then equilibrate (figure 4 does not show this in the magnetisation as it is near the Curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values, the first 20000 steps are ignored so as to get more accurate values for the averages (these changes to the code can be seen in the Montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and goes through a point of inflection. This point of inflection is at the same position as the one that occurs in the magnetisation plot as the average magnetisation per spin goes from 1 down to 0. This point of inflection corresponds to the Curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
=====Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?=====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
the long range fluctuations are &lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
&lt;br /&gt;
=====Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, , the mean of its square , and its squared mean . You may find that the data around the peak is very noisy — this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. =====&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
=====A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns:  (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object=====&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
=====write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report. =====&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
=====Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region. =====&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
=====find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of  for that side length. Make a plot that uses the scaling relation given above to determine . By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate. ======&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796265</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796265"/>
		<updated>2019-11-19T23:08:18Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Accelerating the code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using &#039;for&#039; statements to determine the energy and magnetisation, the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be sped up by using the sum(), roll() and multiply() functions so that not every individual site in the lattice has to be checked individually against every other by using the roll function. It is possible to generate new lattices with the lattice sites moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #lattice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the &#039;for&#039; statements replaced as above, the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0 however change rapidly with in the first 20000 steps and then equilateral (figure 4 dose not show this in the magnetisation as it is near the curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values the first 20000 steps are ignored so as to get a more accurate values for the averages (these changes to the code can be seen in the montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and gose through a point of inflection. This point on in inflection is at the same as the on that occurs in the magnetisation plot as the average magnetisation per spin goes form 1 down to 0. This point of inflection corresponds to the curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
=====Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?=====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
the long range fluctuations are &lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
&lt;br /&gt;
=====Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, , the mean of its square , and its squared mean . You may find that the data around the peak is very noisy — this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. =====&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
=====A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns:  (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object=====&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
=====write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report. =====&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
=====Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region. =====&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
=====find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of  for that side length. Make a plot that uses the scaling relation given above to determine . By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate. ======&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796264</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796264"/>
		<updated>2019-11-19T23:04:48Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Introduction to Monte Carlo simulation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore to get the average energy and magnetization they all need to be calculated individually. If a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a single &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen from above, to calculate the average energy at a specific temperature would take a long long time and the equations do not take into account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice, flipping one and seeing if either the overall energy decreases or if the energy increases.  Then if the probability for the change in state is greater than or equal to a random number between 0 and 1, it is accepted otherwise it is rejected, and the process is repeated until the energy is stabilised, at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo function is created as below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the Curie temperature, it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the Curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using for statements to determine the energy and magnetisation the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be speed up by using the sum(), roll() and multiply() functions so that not every individual sigh in the lattice has to be cheaked individually against every other by using the roll function it is possible to generate new lattice with the lattice points moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #latice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the for statements replaced as above the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0 however change rapidly with in the first 20000 steps and then equilateral (figure 4 dose not show this in the magnetisation as it is near the curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values the first 20000 steps are ignored so as to get a more accurate values for the averages (these changes to the code can be seen in the montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and gose through a point of inflection. This point on in inflection is at the same as the on that occurs in the magnetisation plot as the average magnetisation per spin goes form 1 down to 0. This point of inflection corresponds to the curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
=====Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?=====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
the long range fluctuations are &lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
&lt;br /&gt;
=====Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, , the mean of its square , and its squared mean . You may find that the data around the peak is very noisy — this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. =====&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
=====A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns:  (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object=====&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
=====write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report. =====&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
=====Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region. =====&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
=====find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of  for that side length. Make a plot that uses the scaling relation given above to determine . By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate. ======&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796263</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796263"/>
		<updated>2019-11-19T22:59:34Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* Task 5 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to check that the energy and magnetisation code is outputting the expected values, which is what is seen.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore as to get the average energy and magnetization they all need to be calculated individually, if a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a singl &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen form above to calculate the average energy at a specific temperature would take a long long time and the equations do not take in to account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice and flipping one and seeing if either the overall energy decreases or if the energy increases then if the probability for the change in state is grater than or equal to a random number between 0 and 1 then it is excepted else it is rejected an the process is repeated until the energy is stabilised at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monti Carlo function is created as bellow:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the curie temperature it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using for statements to determine the energy and magnetisation the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be speed up by using the sum(), roll() and multiply() functions so that not every individual sigh in the lattice has to be cheaked individually against every other by using the roll function it is possible to generate new lattice with the lattice points moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #latice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the for statements replaced as above the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0 however change rapidly with in the first 20000 steps and then equilateral (figure 4 dose not show this in the magnetisation as it is near the curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values the first 20000 steps are ignored so as to get a more accurate values for the averages (these changes to the code can be seen in the montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and gose through a point of inflection. This point on in inflection is at the same as the on that occurs in the magnetisation plot as the average magnetisation per spin goes form 1 down to 0. This point of inflection corresponds to the curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
=====Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?=====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
the long range fluctuations are &lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
&lt;br /&gt;
=====Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, , the mean of its square , and its squared mean . You may find that the data around the peak is very noisy — this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. =====&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
=====A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns:  (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object=====&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
=====write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report. =====&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
=====Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region. =====&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
=====find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of  for that side length. Make a plot that uses the scaling relation given above to determine . By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate. ======&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796261</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796261"/>
		<updated>2019-11-19T22:57:59Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* task 3 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins, by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins, the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero, all the spins are in the same direction as the enthalpic driving forces are dominant. This &lt;br /&gt;
means that all of the spins will align, resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
However, if the temperature is raised above the Curie temperature, it is expected that the entropic driving force would dominate, meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to cheack that the energy and magnetisation code is outputting the expected values which as can be seen it is.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore as to get the average energy and magnetization they all need to be calculated individually, if a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a singl &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen form above to calculate the average energy at a specific temperature would take a long long time and the equations do not take in to account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice and flipping one and seeing if either the overall energy decreases or if the energy increases then if the probability for the change in state is grater than or equal to a random number between 0 and 1 then it is excepted else it is rejected an the process is repeated until the energy is stabilised at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monti Carlo function is created as bellow:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the curie temperature it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using for statements to determine the energy and magnetisation the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be speed up by using the sum(), roll() and multiply() functions so that not every individual sigh in the lattice has to be cheaked individually against every other by using the roll function it is possible to generate new lattice with the lattice points moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #latice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the for statements replaced as above the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0 however change rapidly with in the first 20000 steps and then equilateral (figure 4 dose not show this in the magnetisation as it is near the curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values the first 20000 steps are ignored so as to get a more accurate values for the averages (these changes to the code can be seen in the montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and gose through a point of inflection. This point on in inflection is at the same as the on that occurs in the magnetisation plot as the average magnetisation per spin goes form 1 down to 0. This point of inflection corresponds to the curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
=====Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?=====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
the long range fluctuations are &lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
&lt;br /&gt;
=====Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, , the mean of its square , and its squared mean . You may find that the data around the peak is very noisy — this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. =====&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
=====A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns:  (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object=====&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
=====write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report. =====&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
=====Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region. =====&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
=====find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of  for that side length. Make a plot that uses the scaling relation given above to determine . By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate. ======&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796259</id>
		<title>Rep:3rd Y CMP:fgk17</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:3rd_Y_CMP:fgk17&amp;diff=796259"/>
		<updated>2019-11-19T22:54:49Z</updated>

		<summary type="html">&lt;p&gt;Fgk17: /* task 2 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=3&amp;lt;sup&amp;gt;rd&amp;lt;/sup&amp;gt; year CMP comp lab=&lt;br /&gt;
&lt;br /&gt;
==introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
===task 1===&lt;br /&gt;
In the Ising model the lowest energy is given by equation 1: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-DNJ&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 1&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where D is the number of dimensions, N is the total number of spins and J is a constant which controls the strength of interactions. This equation is derived from equation 2:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E=\frac{1}{2}J\Sigma^N_i\Sigma_{j\epsilon neighbours (i)} s_i s_j&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;s_i &amp;lt;/math&amp;gt;and &amp;lt;math&amp;gt; s_j&amp;lt;/math&amp;gt; are the spins of a lattice site and its adjacent lattice sites, and N is the total number of lattice sites. At the lowest possible energy state there are a total of 2 possible configurations with either all of the spins being &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; this means that &amp;lt;math&amp;gt;s_is_j&amp;lt;/math&amp;gt; will always be equal to 1. The summation of all these possible interactions will be twice the sum of all of the actual interactions as they are all counted twice, leading to the &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; at the front of the equation. The total number of interactions is equal to the number of spins times the dimensionality, as for a 2D lattice each spin under goes 4 interactions, however when calculating the E these are all calculated twice leading to the total number of interactions to be DN. &lt;br /&gt;
&lt;br /&gt;
As there are 2 different options for the spin of a lattice site the lowest energy state can either have all of the lattice sites having a spin of &amp;lt;math&amp;gt;+1 &amp;lt;/math&amp;gt;or &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; meaning that the lowest energy state has a multiplicity of 2. The entropy (S) of the lattice is calculated by equation 3, where &amp;lt;math&amp;gt;\Omega&amp;lt;/math&amp;gt; is the multiplicity and k_B is the Boltzmann constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(\Omega)&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entropy of the lowest energy state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=1.38 \times 10^{-23} ln(2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S=9.57\times 10^{-24} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 2===&lt;br /&gt;
&lt;br /&gt;
For a system that is in the lowest energy configuration and has dimensions &amp;lt;math&amp;gt;(D=3,N=1000)&amp;lt;/math&amp;gt; the energy can be calculated using equation 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E = -DNJ=-3(1000)J=-3000J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one of the spins at random flips spontaneously the energy increases. This energy is calculated using equation 2. &lt;br /&gt;
&lt;br /&gt;
In this system each spin under goes a total of 6 different interactions resulting in a total of 6000 interactions with 12 being between a &amp;lt;math&amp;gt;+1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt; spin and the rest being between like spins meaning that &amp;lt;math&amp;gt;\Sigma_{i}^{N}\Sigma_j s_is_j&amp;lt;/math&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;5988(1)(1)+12(1)(-1)=5976&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Which when substituted in to equation 2 gives:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; E=-1/2 (5976)J=-2988J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore the total energy change due to the flip is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E=-3000J--2988J= 12J&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The multiplicity of the state after the flip has occurred is  2000 due to there there being 2 different starting states with either all &amp;lt;math&amp;gt;+1 or -1&amp;lt;/math&amp;gt; spins resulting in 2000 possible resulting arrangements.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt; S=k_B ln(2000)=1.049\times 10^{-22} J/K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gain in entropy between the 2 states is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Delta S=1.049\times 10^{-22}-9.57\times 10^{-24} =9.533 \times 10^{-23} J/K &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===task 3===&lt;br /&gt;
&lt;br /&gt;
The total magnetisation(M) of a system is given by equation 4: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M=\Sigma_i s_i&amp;lt;/math&amp;gt;     &amp;lt;math&amp;gt;equation 4&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
For a 1D lattice with 3 positive spins and 2 negative spins by using equation 4 the magnetisation can be seen to be 1. For a 2D lattice with 13 positive spins and 12 negative spins the overall magnetisation is 1. &lt;br /&gt;
&lt;br /&gt;
If the Ising lattice discussed above is at absolute zero all the spins are in the same direction as the enthalpic driving forces are dominant Meaning that all of the spins will align resulting in the magnetisation being either -1000 or 1000.&lt;br /&gt;
If the temperature however is raised above the curie temperature we expect the entropic driving force to dominate meaning that the expected magnetisation would be around 0.&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
===Task 4===&lt;br /&gt;
energy() functions &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = 0.0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):#runs through all of the lattice sights &lt;br /&gt;
                x=int(i)-1 #finds the lattice sight to the left&lt;br /&gt;
                y=int(j)-1 #finds the lattice sight above&lt;br /&gt;
         &lt;br /&gt;
                energy=energy-self.lattice[x,j]*self.lattice[i,j]-self.lattice[i,y]*self.lattice[i,j]   #calculate the energy of the lattice sights and then add them to the running total of the energy &lt;br /&gt;
        # as only two of the interactions with each lattice sight are looked at the resulting energy dose not need to be divided by 2 &lt;br /&gt;
        return energy &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the value for J in equation 2 is assumed to be 1 at all times as we are working in reduced units where &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
magnetisation() function &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=0&lt;br /&gt;
        for i in range(0,self.n_rows):&lt;br /&gt;
            for j in range(0,self.n_cols):&lt;br /&gt;
                magnetisation=magnetisation+int(self.lattice[i,j]) #runs through all of the lattice sites and adds them together to get the total magnetisation &lt;br /&gt;
        &lt;br /&gt;
        return magnetisation &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 5===&lt;br /&gt;
&lt;br /&gt;
[[File:Cheackfig.png|200px|thumb|right|figure 1: showing expected enrgies and magnetisation for a 4x4 lattice]]&lt;br /&gt;
figure 1 shows the results of the provided ILcheck.py file to cheack that the energy and magnetisation code is outputting the expected values which as can be seen it is.&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
===Task 6===&lt;br /&gt;
&lt;br /&gt;
For a system with 100 different spins there are 2 possible configurations for each spin meaning that the total number of possible configurations is &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; which is the same as &amp;lt;math&amp;gt;1.268\times10^{30}&amp;lt;/math&amp;gt; configurations. Therefore as to get the average energy and magnetization they all need to be calculated individually, if a computer is able to calculate at a rate of &amp;lt;math&amp;gt; 1\times 10^9 &amp;lt;/math&amp;gt;  configurations per second then the total lengh of time to calculate a singl &amp;lt;math&amp;gt;\left\langle M \right\rangle_T&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;1.268\times 10^{21}&amp;lt;/math&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
The average energy and magnetisations are calculated by equations 5 and 6:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle M\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;    &amp;lt;math&amp;gt; equation 5&amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\langle E\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;   &amp;lt;math&amp;gt; equation 6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 7===&lt;br /&gt;
&lt;br /&gt;
As can be seen form above to calculate the average energy at a specific temperature would take a long long time and the equations do not take in to account which arrangement of spins are more likely at a specific temperature. This problem is solved by using the Monte Carlo method. The method works by starting with a completely randomised set of spins in a lattice and flipping one and seeing if either the overall energy decreases or if the energy increases then if the probability for the change in state is grater than or equal to a random number between 0 and 1 then it is excepted else it is rejected an the process is repeated until the energy is stabilised at which point the average energy is taken.&lt;br /&gt;
&lt;br /&gt;
The Monti Carlo function is created as bellow:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step and takes the variable temperature&lt;br /&gt;
        energy_old = self.energy() # the energy of the old state is saved so that it can be used to compare to&lt;br /&gt;
        #the following two lines are used to select the coordinates of the random spin which will be flipped&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]   #spin flipped &lt;br /&gt;
        energy_new=self.energy() #energy of the new lattice calculated&lt;br /&gt;
        D_energy=energy_new-energy_old # difference in energies &lt;br /&gt;
                   &lt;br /&gt;
        #the following line chooses a random number in the rang [0,1)&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        &lt;br /&gt;
        self.n_cycles=self.n_cycles+1 #the cycle counter is updated &lt;br /&gt;
        # if the change in energy is grater than zero and the random number which has been generated is grater than the Boltzmann factor the lattice is returned to the initial state&lt;br /&gt;
        if D_energy&amp;gt;0 and random_number&amp;gt;np.e**(-(D_energy)/T):&lt;br /&gt;
            self.lattice[random_i,random_j]=-self.lattice[random_i,random_j]&lt;br /&gt;
&lt;br /&gt;
        # the sum of the energies and the magnetisations and both of there squares is updated only after 20000 cycles as this gives enables a better estimate of the energies to be calculated &lt;br /&gt;
        if self.n_cycles&amp;gt;20000:&lt;br /&gt;
            self.E=self.E+self.energy()&lt;br /&gt;
            self.E2=self.E2+self.energy()**2&lt;br /&gt;
            self.M=self.M+self.magnetisation()&lt;br /&gt;
            self.M2=self.M2+self.magnetisation()**2&lt;br /&gt;
        #energy is in units of k_B&lt;br /&gt;
        return self.energy(), self.magnetisation()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The statistics function is defined as follows so as to generate &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt; and the step count&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def statistics(self):&lt;br /&gt;
        # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and the step count and returns them&lt;br /&gt;
        n=self.n_cycles #step count&lt;br /&gt;
        E=self.E/(self.n_cycles-20000) #avarage energy after 20000 steps&lt;br /&gt;
        E2=self.E2/(self.n_cycles-20000) #avarage energy squared after 20000 steps&lt;br /&gt;
        M=self.M/(self.n_cycles-20000) #avarage magnetisation after 20000 steps&lt;br /&gt;
        M2=self.M2/(self.n_cycles-20000) #avarage magnetisation squared after 20000 steps&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        return E, E2, M, M2, n&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Task 8===&lt;br /&gt;
[[File:FK P4 T3.png||thumb|right|figure 2: optimisation of an 8 by 8 lattice when T=1, due to the animation file not working properly the resulting values generated by the statistics function where not outputted ]]&lt;br /&gt;
&lt;br /&gt;
If the temperature is below the curie temperature it is expected that spontaneous magnetisation will occur. In figure 2 it can be can see that the curie temperature is above 1.0 as the lattice tends to a constant magnetisation of -1 (all the spins are in the same direction) and the energy tends to -2 per spin (lowest energy state)&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
===Task 8===&lt;br /&gt;
When using for statements to determine the energy and magnetisation the time taken of the code to perform 2000 steps is 7.39323s &amp;lt;math&amp;gt;\pm0.06948s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 9===&lt;br /&gt;
The program can be speed up by using the sum(), roll() and multiply() functions so that not every individual sigh in the lattice has to be cheaked individually against every other by using the roll function it is possible to generate new lattice with the lattice points moved either up or to the left by one. This can then be multiplied by the original lattice to get the spin interaction and then summed to get the total energy.&lt;br /&gt;
The energy function&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = np.sum(np.multiply(np.roll(self.lattice,1,axis=0),self.lattice))+np.sum(np.multiply(np.roll(self.lattice,1,axis=1),self.lattice)) #latice is rolled once upwards and once to the left these are then multiplied by the ordinal lattice and summed so as to take in to account all of the interactions&lt;br /&gt;
        return -energy   #the negative of the energy is returned as the more favourable interaction is between like spins which output positive intergers&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The magnetisation function&lt;br /&gt;
&amp;lt;pre&amp;gt;    &lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice) #the sum of the elements of the lattice is taken&lt;br /&gt;
                        &lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 10===&lt;br /&gt;
With the for statements replaced as above the run time for 2000 Monte Carlo steps is greatly decreased to 0.41342s &amp;lt;math&amp;gt;\pm0.02199s&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
===Task 11===&lt;br /&gt;
The provided ILfinalframe.py script was used to generate figures 3,4 and 5 which are of a 32 by 32 lattice at different temperatures&lt;br /&gt;
[[File:Fk_finalframe32_1.png|200px|thumb|left|figure 3:32x32 lattice optimisation where T=1]]&lt;br /&gt;
[[File:Fk_finalframe32_2.png|200px|thumb|centre|figure 4:32x32 lattice optimisation where T=2]]&lt;br /&gt;
[[File:Fk_finalframe32_5.png|200px|thumb|right|figure 5:32x32 lattice optimisation where T=5]]&lt;br /&gt;
&lt;br /&gt;
from these figures it can be seen that the energy per spin and magnetisation per spin both start a 0 however change rapidly with in the first 20000 steps and then equilateral (figure 4 dose not show this in the magnetisation as it is near the curie temperature and so takes longer to equilibrium). Because the starting values are not near the equilibrium values the first 20000 steps are ignored so as to get a more accurate values for the averages (these changes to the code can be seen in the montecarlostep and statistics functions above).&lt;br /&gt;
&lt;br /&gt;
===Task 12===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Average energy and magnetisation for different lattice sizes for T=0.25 to 5 &lt;br /&gt;
! 2x2&lt;br /&gt;
! 4x4&lt;br /&gt;
! 8X8&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 2X2 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 energy.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 2X2 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 4X4 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 8X8 magnetisation.png|250px]]&lt;br /&gt;
|-&lt;br /&gt;
! 16x16&lt;br /&gt;
! 32x32 &lt;br /&gt;
!&lt;br /&gt;
|- &lt;br /&gt;
| [[File:Fk 16X16 energy.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 energy.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Fk 16X16 magnetisation.png|250px]]&lt;br /&gt;
| [[File:Fk 32X32 magnetisation.png|250px]]&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data is generated using the provided ILtemperature.py file and the error bars were calculated by taking the square root of the variance per spin&lt;br /&gt;
&lt;br /&gt;
As the temperature increases the average energy also increases (those shown in the table are the average energy per spin) and gose through a point of inflection. This point on in inflection is at the same as the on that occurs in the magnetisation plot as the average magnetisation per spin goes form 1 down to 0. This point of inflection corresponds to the curie temperature.&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
===Task 13===&lt;br /&gt;
=====Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?=====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
the long range fluctuations are &lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
===Task 14===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C_v = \frac{\partial \langle E\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \quad \mbox{as previously stated }\quad \langle E\rangle=\frac{1}{z}\sum E e^{-\beta E}=-\frac{1}{z} \frac{\partial z}{\partial \beta}=-\frac{\partial ln(z)}{\partial \beta}, \quad \textrm{as}\quad  \beta=\frac{1}{k_BT}\quad \textrm{and} \quad z=e^{-\beta E}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{and as} \quad \frac{\partial}{\partial T}=\frac{\partial \beta}{\partial T} \frac{\partial}{\partial \beta}=-\frac{1}{k_B T^2} \frac{\partial}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\quad \textrm{then }\quad \frac{\partial \langle E\rangle}{\partial T}=\frac{\partial}{\partial T} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)=\frac{1}{k_BT^2}\left(\frac{\partial^2 ln(z)}{\partial \beta^2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{as} \quad Var[E]=\langle E^2\rangle-\langle E\rangle \quad \mbox{and as} \quad \langle E^2\rangle=\frac{1}{z}\sum E^2 e^{-\beta E}=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\textrm{then} \quad Var[E]=\langle E^2\rangle-\langle E\rangle^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)^2=\frac{1}{z} \frac{\partial^2 z}{\partial \beta^2}-\frac{1}{z^2} \left(\frac{\partial z}{\partial \beta} \right)^2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{by expanding} \quad  \frac{\partial \langle E\rangle}{\partial T}-\frac{1}{k_BT^2} \frac{\partial}{\partial \beta} \left(-\frac{1}{z} \frac{\partial z}{\partial \beta} \right)= \frac{1}{k_BT^2} \left(\frac{\partial \left( \frac{1}{z} \right) }{\partial \beta} \frac{\partial z}{\partial \beta}+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)= \frac{1}{k_BT^2} \left(-\frac{1}{z^2} \left(  \frac{\partial z}{\partial \beta}\right)^2+\frac{1}{z}\frac{\partial^2 z}{\partial \beta^2}\right)=\frac{1}{k_BT^2}\left(\langle E^2\rangle-\langle E\rangle\right) \quad \mbox{as seen above}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{therefore} \quad \frac{\partial \langle E\rangle}{\partial T}=\frac{Var[E]}{k_BT^2}=C_v &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \mbox{this also shows that} \quad \frac{\partial^2 ln(z)}{\partial \beta^2}=\frac{Var[E]}{k_BT^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Task 15 ===&lt;br /&gt;
&lt;br /&gt;
=====Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, , the mean of its square , and its squared mean . You may find that the data around the peak is very noisy — this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. =====&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
===Task 16===&lt;br /&gt;
=====A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns:  (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object=====&lt;br /&gt;
=== Task 17 ===&lt;br /&gt;
=====write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report. =====&lt;br /&gt;
===Task 18 ===&lt;br /&gt;
=====Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region. =====&lt;br /&gt;
&lt;br /&gt;
===Task 19 ===&lt;br /&gt;
=====find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of  for that side length. Make a plot that uses the scaling relation given above to determine . By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate. ======&lt;/div&gt;</summary>
		<author><name>Fgk17</name></author>
	</entry>
</feed>