<?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=Jp3915</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=Jp3915"/>
	<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/wiki/Special:Contributions/Jp3915"/>
	<updated>2026-05-16T14:04:42Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.0</generator>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=644588</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=644588"/>
		<updated>2017-11-21T20:24:20Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK20 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up or all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will change to -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as lattice size increases. It takes around 1500 cycles for a 10x10 lattice and 30000 cycles for a 20x20 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. For a 20x20 lattice, around 3000 cycles are needed, which is 7.5 times of the number of spins in the system. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice using data from 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for averaging 3 repeats:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice using data from 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice using data from 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice using data from 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice using data from 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large error bars showing significant fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_math.png|thumb|center|1000px|Proof for TASK15.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_final.png|thumb|center|1000px|&#039;&#039;&#039;Figure 17.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8ccom.png|thumb|center|1000px|&#039;&#039;&#039;Figure 18.&#039;&#039;&#039; Comparison of heat capacity found by Python simulation and C++ simulation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 37) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;data&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation data of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;16x16_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 19.&#039;&#039;&#039; Polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly2.png|thumb|center|1000px|&#039;&#039;&#039;Figure 20.&#039;&#039;&#039; Modified version of polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
According to equation T&amp;lt;sub&amp;gt;C,L&amp;lt;/sub&amp;gt;=A/L+T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt;, T&amp;lt;sub&amp;gt;C,L&amp;lt;/sub&amp;gt; was plotted against 1/L. T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt;, the Curie temperature for the infinite 2D Ising lattice, is the intercept with y axis. T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt; found using the Python data is 2.202K and literature value found is 2.269K&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;, which is closer to the value found using C++ data (2.279K). There is 3% difference between the experimental value and theoretical value. The major source of error is that temperature interval of 0.1K is not small enough to produce an accurate simulation. 0.1K was chosen due to time limitation but the maximum heat capacity may lay between the sampling temperatures. Data by C++ simulation gives much closer result so more sampling temperature should be used. Also simulations should be carried out for more different lattice sizes to give a better linear fitting. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_done.png|thumb|center|1000px|&#039;&#039;&#039;Figure 21.&#039;&#039;&#039; Linear fitting to find Curie temperature of an infinite lattice.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;REFERENCE&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
[1] L. Onsager, &#039;&#039;Phys. Rev.&#039;&#039;, 1944, &#039;&#039;&#039;65&#039;&#039;&#039;, 117.&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=643366</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=643366"/>
		<updated>2017-11-21T12:28:24Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK14 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up or all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will change to -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as lattice size increases. It takes around 1500 cycles for a 10x10 lattice and 30000 cycles for a 20x20 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. For a 20x20 lattice, around 3000 cycles are needed, which is 7.5 times of the number of spins in the system. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice using data from 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for averaging 3 repeats:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice using data from 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice using data from 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice using data from 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice using data from 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large error bars showing significant fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_math.png|thumb|center|1000px|Proof for TASK15.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_final.png|thumb|center|1000px|&#039;&#039;&#039;Figure 17.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8ccom.png|thumb|center|1000px|&#039;&#039;&#039;Figure 18.&#039;&#039;&#039; Comparison of heat capacity found by Python simulation and C++ simulation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 37) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;data&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation data of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;16x16_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 19.&#039;&#039;&#039; Polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly2.png|thumb|center|1000px|&#039;&#039;&#039;Figure 20.&#039;&#039;&#039; Modified version of polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
According to equation T&amp;lt;sub&amp;gt;C,L&amp;lt;/sub&amp;gt;=A/L+T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt;, T&amp;lt;sub&amp;gt;C,L&amp;lt;/sub&amp;gt; was plotted against 1/L. T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt;, the Curie temperature for the infinite 2D Ising lattice, was found to be the intercept with y axis. T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt; found using the Python data is 2.202K and literature value found is 2.269K&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;, which is closer to the value found using C++ data (2.279K). There is 3% difference between the experimental value and theoretical value. The major source of error is that temperature interval of 0.1K is not small enough to produce an accurate simulation. 0.1K was chosen due to time limitation but the maximum heat capacity may lay between the sampling temperatures. Data by C++ simulation gives much closer result so more sampling temperature should be used. Also simulations should be carried out for more different lattice sizes to give a better linear fitting. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_done.png|thumb|center|1000px|&#039;&#039;&#039;Figure 21.&#039;&#039;&#039; Linear fitting to find Curie temperature of an infinite lattice.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;REFERENCE&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
[1] L. Onsager, &#039;&#039;Phys. Rev.&#039;&#039;, 1944, &#039;&#039;&#039;65&#039;&#039;&#039;, 117.&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=643362</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=643362"/>
		<updated>2017-11-21T12:27:02Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK14 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up or all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will change to -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as lattice size increases. It takes around 1500 cycles for a 10x10 lattice and 30000 cycles for a 20x20 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. For a 20x20 lattice, around 3000 cycles are needed, which is 7.5 times of the number of spins in the system. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice using data from 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for averaging 3 repeats:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice using data from 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice using data from 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice using data from 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice using data from 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_math.png|thumb|center|1000px|Proof for TASK15.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_final.png|thumb|center|1000px|&#039;&#039;&#039;Figure 17.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8ccom.png|thumb|center|1000px|&#039;&#039;&#039;Figure 18.&#039;&#039;&#039; Comparison of heat capacity found by Python simulation and C++ simulation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 37) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;data&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation data of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;16x16_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 19.&#039;&#039;&#039; Polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly2.png|thumb|center|1000px|&#039;&#039;&#039;Figure 20.&#039;&#039;&#039; Modified version of polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
According to equation T&amp;lt;sub&amp;gt;C,L&amp;lt;/sub&amp;gt;=A/L+T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt;, T&amp;lt;sub&amp;gt;C,L&amp;lt;/sub&amp;gt; was plotted against 1/L. T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt;, the Curie temperature for the infinite 2D Ising lattice, was found to be the intercept with y axis. T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt; found using the Python data is 2.202K and literature value found is 2.269K&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;, which is closer to the value found using C++ data (2.279K). There is 3% difference between the experimental value and theoretical value. The major source of error is that temperature interval of 0.1K is not small enough to produce an accurate simulation. 0.1K was chosen due to time limitation but the maximum heat capacity may lay between the sampling temperatures. Data by C++ simulation gives much closer result so more sampling temperature should be used. Also simulations should be carried out for more different lattice sizes to give a better linear fitting. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_done.png|thumb|center|1000px|&#039;&#039;&#039;Figure 21.&#039;&#039;&#039; Linear fitting to find Curie temperature of an infinite lattice.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;REFERENCE&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
[1] L. Onsager, &#039;&#039;Phys. Rev.&#039;&#039;, 1944, &#039;&#039;&#039;65&#039;&#039;&#039;, 117.&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=643353</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=643353"/>
		<updated>2017-11-21T12:23:19Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK13 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up or all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will change to -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as lattice size increases. It takes around 1500 cycles for a 10x10 lattice and 30000 cycles for a 20x20 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. For a 20x20 lattice, around 3000 cycles are needed, which is 7.5 times of the number of spins in the system. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice using data from 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for averaging 3 repeats:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_math.png|thumb|center|1000px|Proof for TASK15.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_final.png|thumb|center|1000px|&#039;&#039;&#039;Figure 17.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8ccom.png|thumb|center|1000px|&#039;&#039;&#039;Figure 18.&#039;&#039;&#039; Comparison of heat capacity found by Python simulation and C++ simulation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 37) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;data&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation data of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;16x16_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 19.&#039;&#039;&#039; Polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly2.png|thumb|center|1000px|&#039;&#039;&#039;Figure 20.&#039;&#039;&#039; Modified version of polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
According to equation T&amp;lt;sub&amp;gt;C,L&amp;lt;/sub&amp;gt;=A/L+T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt;, T&amp;lt;sub&amp;gt;C,L&amp;lt;/sub&amp;gt; was plotted against 1/L. T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt;, the Curie temperature for the infinite 2D Ising lattice, was found to be the intercept with y axis. T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt; found using the Python data is 2.202K and literature value found is 2.269K&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;, which is closer to the value found using C++ data (2.279K). There is 3% difference between the experimental value and theoretical value. The major source of error is that temperature interval of 0.1K is not small enough to produce an accurate simulation. 0.1K was chosen due to time limitation but the maximum heat capacity may lay between the sampling temperatures. Data by C++ simulation gives much closer result so more sampling temperature should be used. Also simulations should be carried out for more different lattice sizes to give a better linear fitting. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_done.png|thumb|center|1000px|&#039;&#039;&#039;Figure 21.&#039;&#039;&#039; Linear fitting to find Curie temperature of an infinite lattice.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;REFERENCE&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
[1] L. Onsager, &#039;&#039;Phys. Rev.&#039;&#039;, 1944, &#039;&#039;&#039;65&#039;&#039;&#039;, 117.&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=643346</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=643346"/>
		<updated>2017-11-21T12:19:12Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK12 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up or all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will change to -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as lattice size increases. It takes around 1500 cycles for a 10x10 lattice and 30000 cycles for a 20x20 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. For a 20x20 lattice, around 3000 cycles are needed, which is 7.5 times of the number of spins in the system. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for averaging 3 repeats:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_math.png|thumb|center|1000px|Proof for TASK15.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_final.png|thumb|center|1000px|&#039;&#039;&#039;Figure 17.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8ccom.png|thumb|center|1000px|&#039;&#039;&#039;Figure 18.&#039;&#039;&#039; Comparison of heat capacity found by Python simulation and C++ simulation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 37) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;data&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation data of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;16x16_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 19.&#039;&#039;&#039; Polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly2.png|thumb|center|1000px|&#039;&#039;&#039;Figure 20.&#039;&#039;&#039; Modified version of polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
According to equation T&amp;lt;sub&amp;gt;C,L&amp;lt;/sub&amp;gt;=A/L+T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt;, T&amp;lt;sub&amp;gt;C,L&amp;lt;/sub&amp;gt; was plotted against 1/L. T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt;, the Curie temperature for the infinite 2D Ising lattice, was found to be the intercept with y axis. T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt; found using the Python data is 2.202K and literature value found is 2.269K&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;, which is closer to the value found using C++ data (2.279K). There is 3% difference between the experimental value and theoretical value. The major source of error is that temperature interval of 0.1K is not small enough to produce an accurate simulation. 0.1K was chosen due to time limitation but the maximum heat capacity may lay between the sampling temperatures. Data by C++ simulation gives much closer result so more sampling temperature should be used. Also simulations should be carried out for more different lattice sizes to give a better linear fitting. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_done.png|thumb|center|1000px|&#039;&#039;&#039;Figure 21.&#039;&#039;&#039; Linear fitting to find Curie temperature of an infinite lattice.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;REFERENCE&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
[1] L. Onsager, &#039;&#039;Phys. Rev.&#039;&#039;, 1944, &#039;&#039;&#039;65&#039;&#039;&#039;, 117.&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=643332</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=643332"/>
		<updated>2017-11-21T12:10:56Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK12 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up or all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will change to -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as temperature increases. It takes around 1500 cycles and 30000 cycles for a 10x10 lattice and 20x20 lattice respectively to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. For a 20x20 lattice, around 3000 cycles are needed, which is 7.5 times of the number of spins in the system. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for averaging 3 repeats:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_math.png|thumb|center|1000px|Proof for TASK15.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_final.png|thumb|center|1000px|&#039;&#039;&#039;Figure 17.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8ccom.png|thumb|center|1000px|&#039;&#039;&#039;Figure 18.&#039;&#039;&#039; Comparison of heat capacity found by Python simulation and C++ simulation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 37) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;data&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation data of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;16x16_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 19.&#039;&#039;&#039; Polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly2.png|thumb|center|1000px|&#039;&#039;&#039;Figure 20.&#039;&#039;&#039; Modified version of polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
According to equation T&amp;lt;sub&amp;gt;C,L&amp;lt;/sub&amp;gt;=A/L+T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt;, T&amp;lt;sub&amp;gt;C,L&amp;lt;/sub&amp;gt; was plotted against 1/L. T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt;, the Curie temperature for the infinite 2D Ising lattice, was found to be the intercept with y axis. T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt; found using the Python data is 2.202K and literature value found is 2.269K&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;, which is closer to the value found using C++ data (2.279K). There is 3% difference between the experimental value and theoretical value. The major source of error is that temperature interval of 0.1K is not small enough to produce an accurate simulation. 0.1K was chosen due to time limitation but the maximum heat capacity may lay between the sampling temperatures. Data by C++ simulation gives much closer result so more sampling temperature should be used. Also simulations should be carried out for more different lattice sizes to give a better linear fitting. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_done.png|thumb|center|1000px|&#039;&#039;&#039;Figure 21.&#039;&#039;&#039; Linear fitting to find Curie temperature of an infinite lattice.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;REFERENCE&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
[1] L. Onsager, &#039;&#039;Phys. Rev.&#039;&#039;, 1944, &#039;&#039;&#039;65&#039;&#039;&#039;, 117.&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=643221</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=643221"/>
		<updated>2017-11-21T10:19:13Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up or all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will change to -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as temperature increases. It takes around 1500 cycles and 30000 cycles for a 10x10 lattice and 20x20 lattice respectively to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. For a 20x20 lattice, first 80000 cycles are ignored and this is outside the range when the system is not yet at equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for averaging 3 repeats:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_math.png|thumb|center|1000px|Proof for TASK15.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_final.png|thumb|center|1000px|&#039;&#039;&#039;Figure 17.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8ccom.png|thumb|center|1000px|&#039;&#039;&#039;Figure 18.&#039;&#039;&#039; Comparison of heat capacity found by Python simulation and C++ simulation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 37) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;data&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation data of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;16x16_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 19.&#039;&#039;&#039; Polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly2.png|thumb|center|1000px|&#039;&#039;&#039;Figure 20.&#039;&#039;&#039; Modified version of polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
According to equation T&amp;lt;sub&amp;gt;C,L&amp;lt;/sub&amp;gt;=A/L+T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt;, T&amp;lt;sub&amp;gt;C,L&amp;lt;/sub&amp;gt; was plotted against 1/L. T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt;, the Curie temperature for the infinite 2D Ising lattice, was found to be the intercept with y axis. T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt; found using the Python data is 2.202K and literature value found is 2.269K&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;, which is closer to the value found using C++ data (2.279K). There is 3% difference between the experimental value and theoretical value. The major source of error is that temperature interval of 0.1K is not small enough to produce an accurate simulation. 0.1K was chosen due to time limitation but the maximum heat capacity may lay between the sampling temperatures. Data by C++ simulation gives much closer result so more sampling temperature should be used. Also simulations should be carried out for more different lattice sizes to give a better linear fitting. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_done.png|thumb|center|1000px|&#039;&#039;&#039;Figure 21.&#039;&#039;&#039; Linear fitting to find Curie temperature of an infinite lattice.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;REFERENCE&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
[1] L. Onsager, &#039;&#039;Phys. Rev.&#039;&#039;, 1944, &#039;&#039;&#039;65&#039;&#039;&#039;, 117.&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915_math.png&amp;diff=643217</id>
		<title>File:JP3915 math.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915_math.png&amp;diff=643217"/>
		<updated>2017-11-21T10:17:30Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=642498</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=642498"/>
		<updated>2017-11-20T16:37:19Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK2 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up or all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will change to -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as temperature increases. It takes around 1500 cycles and 30000 cycles for a 10x10 lattice and 20x20 lattice respectively to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. For a 20x20 lattice, first 80000 cycles are ignored and this is outside the range when the system is not yet at equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for averaging 3 repeats:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_final.png|thumb|center|1000px|&#039;&#039;&#039;Figure 17.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8ccom.png|thumb|center|1000px|&#039;&#039;&#039;Figure 18.&#039;&#039;&#039; Comparison of heat capacity found by Python simulation and C++ simulation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 37) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;data&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation data of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;16x16_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 19.&#039;&#039;&#039; Polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly2.png|thumb|center|1000px|&#039;&#039;&#039;Figure 20.&#039;&#039;&#039; Modified version of polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
According to equation T&amp;lt;sub&amp;gt;C,L&amp;lt;/sub&amp;gt;=A/L+T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt;, T&amp;lt;sub&amp;gt;C,L&amp;lt;/sub&amp;gt; was plotted against 1/L. T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt;, the Curie temperature for the infinite 2D Ising lattice, was found to be the intercept with y axis. T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt; found using the Python data is 2.202K and literature value found is 2.269K&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;, which is closer to the value found using C++ data (2.279K). There is 3% difference between the experimental value and theoretical value. The major source of error is that temperature interval of 0.1K is not small enough to produce an accurate simulation. 0.1K was chosen due to time limitation but the maximum heat capacity may lay between the sampling temperatures. Data by C++ simulation gives much closer result so more sampling temperature should be used. Also simulations should be carried out for more different lattice sizes to give a better linear fitting. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_done.png|thumb|center|1000px|&#039;&#039;&#039;Figure 21.&#039;&#039;&#039; Linear fitting to find Curie temperature of an infinite lattice.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;REFERENCE&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
[1] L. Onsager, &#039;&#039;Phys. Rev.&#039;&#039;, 1944, &#039;&#039;&#039;65&#039;&#039;&#039;, 117.&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=642497</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=642497"/>
		<updated>2017-11-20T16:36:35Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK1 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up or all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will now be -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as temperature increases. It takes around 1500 cycles and 30000 cycles for a 10x10 lattice and 20x20 lattice respectively to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. For a 20x20 lattice, first 80000 cycles are ignored and this is outside the range when the system is not yet at equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for averaging 3 repeats:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_final.png|thumb|center|1000px|&#039;&#039;&#039;Figure 17.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8ccom.png|thumb|center|1000px|&#039;&#039;&#039;Figure 18.&#039;&#039;&#039; Comparison of heat capacity found by Python simulation and C++ simulation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 37) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;data&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation data of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;16x16_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 19.&#039;&#039;&#039; Polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly2.png|thumb|center|1000px|&#039;&#039;&#039;Figure 20.&#039;&#039;&#039; Modified version of polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
According to equation T&amp;lt;sub&amp;gt;C,L&amp;lt;/sub&amp;gt;=A/L+T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt;, T&amp;lt;sub&amp;gt;C,L&amp;lt;/sub&amp;gt; was plotted against 1/L. T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt;, the Curie temperature for the infinite 2D Ising lattice, was found to be the intercept with y axis. T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt; found using the Python data is 2.202K and literature value found is 2.269K&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;, which is closer to the value found using C++ data (2.279K). There is 3% difference between the experimental value and theoretical value. The major source of error is that temperature interval of 0.1K is not small enough to produce an accurate simulation. 0.1K was chosen due to time limitation but the maximum heat capacity may lay between the sampling temperatures. Data by C++ simulation gives much closer result so more sampling temperature should be used. Also simulations should be carried out for more different lattice sizes to give a better linear fitting. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_done.png|thumb|center|1000px|&#039;&#039;&#039;Figure 21.&#039;&#039;&#039; Linear fitting to find Curie temperature of an infinite lattice.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;REFERENCE&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
[1] L. Onsager, &#039;&#039;Phys. Rev.&#039;&#039;, 1944, &#039;&#039;&#039;65&#039;&#039;&#039;, 117.&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=642470</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=642470"/>
		<updated>2017-11-20T16:19:09Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK20 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up and all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will now be -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as temperature increases. It takes around 1500 cycles and 30000 cycles for a 10x10 lattice and 20x20 lattice respectively to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. For a 20x20 lattice, first 80000 cycles are ignored and this is outside the range when the system is not yet at equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for averaging 3 repeats:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_final.png|thumb|center|1000px|&#039;&#039;&#039;Figure 17.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8ccom.png|thumb|center|1000px|&#039;&#039;&#039;Figure 18.&#039;&#039;&#039; Comparison of heat capacity found by Python simulation and C++ simulation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 37) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;data&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation data of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;16x16_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 19.&#039;&#039;&#039; Polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly2.png|thumb|center|1000px|&#039;&#039;&#039;Figure 20.&#039;&#039;&#039; Modified version of polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
According to equation T&amp;lt;sub&amp;gt;C,L&amp;lt;/sub&amp;gt;=A/L+T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt;, T&amp;lt;sub&amp;gt;C,L&amp;lt;/sub&amp;gt; was plotted against 1/L. T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt;, the Curie temperature for the infinite 2D Ising lattice, was found to be the intercept with y axis. T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt; found using the Python data is 2.202K and literature value found is 2.269K&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;, which is closer to the value found using C++ data (2.279K). There is 3% difference between the experimental value and theoretical value. The major source of error is that temperature interval of 0.1K is not small enough to produce an accurate simulation. 0.1K was chosen due to time limitation but the maximum heat capacity may lay between the sampling temperatures. Data by C++ simulation gives much closer result so more sampling temperature should be used. Also simulations should be carried out for more different lattice sizes to give a better linear fitting. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_done.png|thumb|center|1000px|&#039;&#039;&#039;Figure 21.&#039;&#039;&#039; Linear fitting to find Curie temperature of an infinite lattice.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;REFERENCE&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
[1] L. Onsager, &#039;&#039;Phys. Rev.&#039;&#039;, 1944, &#039;&#039;&#039;65&#039;&#039;&#039;, 117.&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=642434</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=642434"/>
		<updated>2017-11-20T16:02:21Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* REFERENCE */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up and all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will now be -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as temperature increases. It takes around 1500 cycles and 30000 cycles for a 10x10 lattice and 20x20 lattice respectively to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. For a 20x20 lattice, first 80000 cycles are ignored and this is outside the range when the system is not yet at equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for averaging 3 repeats:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_final.png|thumb|center|1000px|&#039;&#039;&#039;Figure 17.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8ccom.png|thumb|center|1000px|&#039;&#039;&#039;Figure 18.&#039;&#039;&#039; Comparison of heat capacity found by Python simulation and C++ simulation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 37) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;data&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation data of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;16x16_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 19.&#039;&#039;&#039; Polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly2.png|thumb|center|1000px|&#039;&#039;&#039;Figure 20.&#039;&#039;&#039; Modified version of polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
According to equation T&amp;lt;sub&amp;gt;C,L&amp;lt;/sub&amp;gt;=A/L+T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt;, T&amp;lt;sub&amp;gt;C,L&amp;lt;/sub&amp;gt; was plotted against 1/L. T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt;, the Curie temperature for the infinite 2D Ising lattice, was found to be the intercept with y axis. T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt; found using the Python data is 2.202K and literature value found is 2.269K&amp;lt;sup&amp;gt;[1][2]&amp;lt;/sup&amp;gt;, which is closer to the value found using C++ data (2.279K). There is 3% difference &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_done.png|thumb|center|1000px|&#039;&#039;&#039;Figure 21.&#039;&#039;&#039; Linear fitting to find Curie temperature of an infinite lattice.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;REFERENCE&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
[1] L. Onsager, &#039;&#039;Phys. Rev.&#039;&#039;, 1944, &#039;&#039;&#039;65&#039;&#039;&#039;, 117.&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=642431</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=642431"/>
		<updated>2017-11-20T16:01:54Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up and all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will now be -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as temperature increases. It takes around 1500 cycles and 30000 cycles for a 10x10 lattice and 20x20 lattice respectively to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. For a 20x20 lattice, first 80000 cycles are ignored and this is outside the range when the system is not yet at equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for averaging 3 repeats:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_final.png|thumb|center|1000px|&#039;&#039;&#039;Figure 17.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8ccom.png|thumb|center|1000px|&#039;&#039;&#039;Figure 18.&#039;&#039;&#039; Comparison of heat capacity found by Python simulation and C++ simulation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 37) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;data&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation data of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;16x16_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 19.&#039;&#039;&#039; Polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly2.png|thumb|center|1000px|&#039;&#039;&#039;Figure 20.&#039;&#039;&#039; Modified version of polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
According to equation T&amp;lt;sub&amp;gt;C,L&amp;lt;/sub&amp;gt;=A/L+T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt;, T&amp;lt;sub&amp;gt;C,L&amp;lt;/sub&amp;gt; was plotted against 1/L. T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt;, the Curie temperature for the infinite 2D Ising lattice, was found to be the intercept with y axis. T&amp;lt;sub&amp;gt;C,∞&amp;lt;/sub&amp;gt; found using the Python data is 2.202K and literature value found is 2.269K&amp;lt;sup&amp;gt;[1][2]&amp;lt;/sup&amp;gt;, which is closer to the value found using C++ data (2.279K). There is 3% difference &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_done.png|thumb|center|1000px|&#039;&#039;&#039;Figure 21.&#039;&#039;&#039; Linear fitting to find Curie temperature of an infinite lattice.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;REFERENCE&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
[1]L. Onsager, &#039;&#039;Phys. Rev.&#039;&#039;, 1944, &#039;&#039;&#039;65&#039;&#039;&#039;, 117.&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915_done.png&amp;diff=642392</id>
		<title>File:JP3915 done.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915_done.png&amp;diff=642392"/>
		<updated>2017-11-20T15:46:15Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641885</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641885"/>
		<updated>2017-11-19T18:31:44Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK16 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up and all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will now be -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as temperature increases. It takes around 1500 cycles and 30000 cycles for a 10x10 lattice and 20x20 lattice respectively to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. For a 20x20 lattice, first 80000 cycles are ignored and this is outside the range when the system is not yet at equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for averaging 3 repeats:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_final.png|thumb|center|1000px|&#039;&#039;&#039;Figure 17.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8ccom.png|thumb|center|1000px|&#039;&#039;&#039;Figure 18.&#039;&#039;&#039; Comparison of heat capacity found by Python simulation and C++ simulation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 37) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;data&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation data of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;16x16_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 19.&#039;&#039;&#039; Polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly2.png|thumb|center|1000px|&#039;&#039;&#039;Figure 20.&#039;&#039;&#039; Modified version of polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;REFERENCE&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915_final.png&amp;diff=641884</id>
		<title>File:JP3915 final.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915_final.png&amp;diff=641884"/>
		<updated>2017-11-19T18:31:16Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641876</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641876"/>
		<updated>2017-11-19T18:26:32Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up and all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will now be -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as temperature increases. It takes around 1500 cycles and 30000 cycles for a 10x10 lattice and 20x20 lattice respectively to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. For a 20x20 lattice, first 80000 cycles are ignored and this is outside the range when the system is not yet at equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for averaging 3 repeats:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_cown.png|thumb|center|1000px|&#039;&#039;&#039;Figure 17.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8ccom.png|thumb|center|1000px|&#039;&#039;&#039;Figure 18.&#039;&#039;&#039; Comparison of heat capacity found by Python simulation and C++ simulation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 37) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;data&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation data of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;16x16_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 19.&#039;&#039;&#039; Polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_newpoly2.png|thumb|center|1000px|&#039;&#039;&#039;Figure 20.&#039;&#039;&#039; Modified version of polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;REFERENCE&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915_newpoly2.png&amp;diff=641875</id>
		<title>File:JP3915 newpoly2.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915_newpoly2.png&amp;diff=641875"/>
		<updated>2017-11-19T18:26:04Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915_newpoly1.png&amp;diff=641873</id>
		<title>File:JP3915 newpoly1.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915_newpoly1.png&amp;diff=641873"/>
		<updated>2017-11-19T18:25:49Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641857</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641857"/>
		<updated>2017-11-19T18:14:47Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up and all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will now be -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as temperature increases. It takes around 1500 cycles and 30000 cycles for a 10x10 lattice and 20x20 lattice respectively to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. For a 20x20 lattice, first 80000 cycles are ignored and this is outside the range when the system is not yet at equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for averaging 3 repeats:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_cown.png|thumb|center|1000px|&#039;&#039;&#039;Figure 17.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8ccom.png|thumb|center|1000px|&#039;&#039;&#039;Figure 18.&#039;&#039;&#039; Comparison of heat capacity found by Python simulation and C++ simulation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 37) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;data&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation data of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;16x16_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_poly1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 19.&#039;&#039;&#039; Polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_poly2.png|thumb|center|1000px|&#039;&#039;&#039;Figure 20.&#039;&#039;&#039; Modified version of polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;REFERENCE&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641854</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641854"/>
		<updated>2017-11-19T18:12:42Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK19 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up and all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will now be -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as temperature increases. It takes around 1500 cycles and 30000 cycles for a 10x10 lattice and 20x20 lattice respectively to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. For a 20x20 lattice, first 80000 cycles are ignored and this is outside the range when the system is not yet at equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for averaging 3 repeats:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_cown.png|thumb|center|1000px|&#039;&#039;&#039;Figure 17.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8ccom.png|thumb|center|1000px|&#039;&#039;&#039;Figure 18.&#039;&#039;&#039; Comparison of heat capacity found by Python simulation and C++ simulation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 37) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;data&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation data of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;16x16_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_poly1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 19.&#039;&#039;&#039; Polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_poly2.png|thumb|center|1000px|&#039;&#039;&#039;Figure 20.&#039;&#039;&#039; Modified version of polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641852</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641852"/>
		<updated>2017-11-19T18:11:07Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK18 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up and all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will now be -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as temperature increases. It takes around 1500 cycles and 30000 cycles for a 10x10 lattice and 20x20 lattice respectively to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. For a 20x20 lattice, first 80000 cycles are ignored and this is outside the range when the system is not yet at equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for averaging 3 repeats:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_cown.png|thumb|center|1000px|&#039;&#039;&#039;Figure 17.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8ccom.png|thumb|center|1000px|&#039;&#039;&#039;Figure 18.&#039;&#039;&#039; Comparison of heat capacity found by Python simulation and C++ simulation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 37) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;data&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation data of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;16x16_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_poly1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 19.&#039;&#039;&#039; Polyfit of 37th order polynomial.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641850</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641850"/>
		<updated>2017-11-19T18:09:43Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK18 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up and all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will now be -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as temperature increases. It takes around 1500 cycles and 30000 cycles for a 10x10 lattice and 20x20 lattice respectively to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. For a 20x20 lattice, first 80000 cycles are ignored and this is outside the range when the system is not yet at equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for averaging 3 repeats:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_cown.png|thumb|center|1000px|&#039;&#039;&#039;Figure 17.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8ccom.png|thumb|center|1000px|&#039;&#039;&#039;Figure 18.&#039;&#039;&#039; Comparison of heat capacity found by Python simulation and C++ simulation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 101) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;data&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation data of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;8x8c.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_poly1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 19.&#039;&#039;&#039; Polyfit.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915_poly2.png&amp;diff=641848</id>
		<title>File:JP3915 poly2.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915_poly2.png&amp;diff=641848"/>
		<updated>2017-11-19T18:08:13Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915_poly1.png&amp;diff=641847</id>
		<title>File:JP3915 poly1.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915_poly1.png&amp;diff=641847"/>
		<updated>2017-11-19T18:07:54Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641826</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641826"/>
		<updated>2017-11-19T17:52:56Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK18 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up and all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will now be -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as temperature increases. It takes around 1500 cycles and 30000 cycles for a 10x10 lattice and 20x20 lattice respectively to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. For a 20x20 lattice, first 80000 cycles are ignored and this is outside the range when the system is not yet at equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for averaging 3 repeats:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_cown.png|thumb|center|1000px|&#039;&#039;&#039;Figure 17.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8ccom.png|thumb|center|1000px|&#039;&#039;&#039;Figure 18.&#039;&#039;&#039; Comparison of heat capacity found by Python simulation and C++ simulation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 101) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;data&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation data of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;8x8c.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_polyfit1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 19.&#039;&#039;&#039; Polyfit.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641824</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641824"/>
		<updated>2017-11-19T17:51:28Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK18 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up and all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will now be -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as temperature increases. It takes around 1500 cycles and 30000 cycles for a 10x10 lattice and 20x20 lattice respectively to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. For a 20x20 lattice, first 80000 cycles are ignored and this is outside the range when the system is not yet at equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for averaging 3 repeats:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_cown.png|thumb|center|1000px|&#039;&#039;&#039;Figure 17.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8ccom.png|thumb|center|1000px|&#039;&#039;&#039;Figure 18.&#039;&#039;&#039; Comparison of heat capacity found by Python simulation and C++ simulation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 101) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;data&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by C++ of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;8x8c.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_polyfit1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 19.&#039;&#039;&#039; Polyfit.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641822</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641822"/>
		<updated>2017-11-19T17:49:53Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK18 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up and all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will now be -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as temperature increases. It takes around 1500 cycles and 30000 cycles for a 10x10 lattice and 20x20 lattice respectively to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. For a 20x20 lattice, first 80000 cycles are ignored and this is outside the range when the system is not yet at equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for averaging 3 repeats:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_cown.png|thumb|center|1000px|&#039;&#039;&#039;Figure 17.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8ccom.png|thumb|center|1000px|&#039;&#039;&#039;Figure 18.&#039;&#039;&#039; Comparison of heat capacity found by Python simulation and C++ simulation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 101) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;data&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by C++ of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;8x8c.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641821</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641821"/>
		<updated>2017-11-19T17:48:43Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK17 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up and all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will now be -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as temperature increases. It takes around 1500 cycles and 30000 cycles for a 10x10 lattice and 20x20 lattice respectively to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. For a 20x20 lattice, first 80000 cycles are ignored and this is outside the range when the system is not yet at equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for averaging 3 repeats:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_cown.png|thumb|center|1000px|&#039;&#039;&#039;Figure 17.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8ccom.png|thumb|center|1000px|&#039;&#039;&#039;Figure 18.&#039;&#039;&#039; Comparison of heat capacity found by Python simulation and C++ simulation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 101) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by C++&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by C++ of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;8x8c.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641814</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641814"/>
		<updated>2017-11-19T17:37:46Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK16 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up and all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will now be -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as temperature increases. It takes around 1500 cycles and 30000 cycles for a 10x10 lattice and 20x20 lattice respectively to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. For a 20x20 lattice, first 80000 cycles are ignored and this is outside the range when the system is not yet at equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for averaging 3 repeats:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_cown.png|thumb|center|1000px|&#039;&#039;&#039;Figure 17.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 101) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by C++&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by C++ of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;8x8c.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641800</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641800"/>
		<updated>2017-11-19T17:31:19Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK14 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up and all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will now be -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as temperature increases. It takes around 1500 cycles and 30000 cycles for a 10x10 lattice and 20x20 lattice respectively to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. For a 20x20 lattice, first 80000 cycles are ignored and this is outside the range when the system is not yet at equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for averaging 3 repeats:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_cown.png|thumb|center|1000px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 101) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by C++&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by C++ of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;8x8c.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641799</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641799"/>
		<updated>2017-11-19T17:30:48Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK14 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up and all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will now be -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as temperature increases. It takes around 1500 cycles and 30000 cycles for a 10x10 lattice and 20x20 lattice respectively to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. For a 20x20 lattice, first 80000 cycles are ignored and this is outside the range when the system is not yet at equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
#for averaging 3 repeats&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_cown.png|thumb|center|1000px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 101) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by C++&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by C++ of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;8x8c.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641798</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641798"/>
		<updated>2017-11-19T17:30:30Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK14 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up and all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will now be -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as temperature increases. It takes around 1500 cycles and 30000 cycles for a 10x10 lattice and 20x20 lattice respectively to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. For a 20x20 lattice, first 80000 cycles are ignored and this is outside the range when the system is not yet at equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;for averaging 3 repeats&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_cown.png|thumb|center|1000px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 101) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by C++&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by C++ of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;8x8c.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641794</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641794"/>
		<updated>2017-11-19T17:29:13Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK13 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up and all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will now be -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as temperature increases. It takes around 1500 cycles and 30000 cycles for a 10x10 lattice and 20x20 lattice respectively to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. For a 20x20 lattice, first 80000 cycles are ignored and this is outside the range when the system is not yet at equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuate more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_cown.png|thumb|center|1000px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 101) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by C++&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by C++ of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;8x8c.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641791</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641791"/>
		<updated>2017-11-19T17:28:56Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK14 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up and all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will now be -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as temperature increases. It takes around 1500 cycles and 30000 cycles for a 10x10 lattice and 20x20 lattice respectively to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. For a 20x20 lattice, first 80000 cycles are ignored and this is outside the range when the system is not yet at equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuates more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used for 2x2, 4x4, 8x8 lattice. 300000 runtime was used for 16x16 lattice. 500000 runtime was used for 32x32 lattice. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 13.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 14.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 15.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 16.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
Energy and magnetisation fluctuate more at higher temperature. There is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition. 2x2 and 4x4 are too small for a good simulation because they both have large fluctuations at high temperature. 16x16 will be a good lattice size to use in order to capture a long range fluctuation considering less simulation time is wanted.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_cown.png|thumb|center|1000px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 101) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by C++&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by C++ of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;8x8c.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641785</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641785"/>
		<updated>2017-11-19T17:21:09Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK13 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up and all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will now be -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as temperature increases. It takes around 1500 cycles and 30000 cycles for a 10x10 lattice and 20x20 lattice respectively to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. For a 20x20 lattice, first 80000 cycles are ignored and this is outside the range when the system is not yet at equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it can be seen from figure 7 that energy and magnetisation fluctuates more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_cown.png|thumb|center|1000px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 101) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by C++&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by C++ of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;8x8c.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641783</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641783"/>
		<updated>2017-11-19T17:20:49Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK13 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up and all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will now be -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as temperature increases. It takes around 1500 cycles and 30000 cycles for a 10x10 lattice and 20x20 lattice respectively to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. For a 20x20 lattice, first 80000 cycles are ignored and this is outside the range when the system is not yet at equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
100000 runtime was used and simulation was carried out from 0.3K to 5.0K with interval of 0.1K. 3 repeats were carried out. &lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
As it van be seen from figure 7 that energy and magnetisation fluctuates more at higher temperature because of greater probability of spin flipping. 3 repeats were carried out because there is no guarantee that every configuration will reach equilibrium. As it can be seen from figure 8 that there is no significant difference between energy of each configuration but magnetisation varies significantly during phase transition.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_cown.png|thumb|center|1000px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 101) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by C++&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by C++ of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;8x8c.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641764</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641764"/>
		<updated>2017-11-19T17:03:58Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK12 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up and all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will now be -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K. It takes around 500 cycles for a 8x8 lattice to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Time used to reach equilibrium varies as temperature changes. Energy and magnetisation fluctuate more as temperature increases because there are greater probability for a spin flipping at high temperature. ]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare3.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Time used to reach equilibrium increases as temperature increases. It takes around 1500 cycles and 30000 cycles for a 10x10 lattice and 20x20 lattice respectively to reach equilibrium at 1K.]]&lt;br /&gt;
&lt;br /&gt;
It takes more cylces for a larger lattice to reach equilibrium. A compromise has to be made between less simulation time and more accurate simulation. So instead of using a constant number as N, 200 times of number of spin is chosen as the number of cycles to ignore before the system reaches equilibrium. For a 20x20 lattice, first 80000 cycles are ignored and this is outside the range when the system is not yet at equilibrium. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_cown.png|thumb|center|1000px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 101) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by C++&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by C++ of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;8x8c.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915_compare3.png&amp;diff=641744</id>
		<title>File:JP3915 compare3.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915_compare3.png&amp;diff=641744"/>
		<updated>2017-11-19T16:43:59Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641706</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641706"/>
		<updated>2017-11-19T16:25:17Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK12 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up and all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will now be -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t1s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare1.png|thumb|center|1000px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_compare2.png|thumb|center|1000px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice at 1K.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_cown.png|thumb|center|1000px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 101) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by C++&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by C++ of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;8x8c.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915_compare2.png&amp;diff=641663</id>
		<title>File:JP3915 compare2.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915_compare2.png&amp;diff=641663"/>
		<updated>2017-11-19T16:09:55Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915_compare1.png&amp;diff=641661</id>
		<title>File:JP3915 compare1.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915_compare1.png&amp;diff=641661"/>
		<updated>2017-11-19T16:09:38Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641621</id>
		<title>Rep:Mod:jp3915Y3CMP</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:jp3915Y3CMP&amp;diff=641621"/>
		<updated>2017-11-19T15:53:44Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: /* TASK12 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Monte-Carlo simulations of a 2D Ising Model&#039;&#039;&#039; =&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK1&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy state is when all of the spins are pointing at the same direction i.e. all up (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=+1) or all down (all s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;=-1). At this state s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will be 1. Each spin has 2D neighbours. E = -0.5*J*N*2D = -DNJ. Multiplicity is 2 because there are two possible configurations of the lowest possible energy state, all up and all down. S = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;lnΩ = K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2 = 9.565*10&amp;lt;sup&amp;gt;-24&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK2&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In a 3D lattice, each spin has 6 neighbours. When flipping a spin, s&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;*s&amp;lt;sub&amp;gt;j&amp;lt;/sub&amp;gt; will now be -1. The contribution to energy of this spin will change from +6J to -6J. So the change in energy is 12J. The number of  possible configurations of this state is now 2*1000!/999!=2000 (times by two because original state can be all up or all down). The new entropy is K&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;*ln2000=1.049*10&amp;lt;sup&amp;gt;-22&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt; and the increase in entropy is 9.533*10&amp;lt;sup&amp;gt;-23&amp;lt;/sup&amp;gt; JK&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK3&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
M&amp;lt;sub&amp;gt;1D&amp;lt;/sub&amp;gt;=+1. M&amp;lt;sub&amp;gt;2D&amp;lt;/sub&amp;gt;=+1. At absolute zero, the energetic driving force dominates and all the spins will be parallel. M&amp;lt;sub&amp;gt;3D&amp;lt;/sub&amp;gt;=±1000 at T=0K.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK4&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        for i in range(len(self.lattice)):&lt;br /&gt;
            for j in range(len(self.lattice)):&lt;br /&gt;
                s=self.lattice[i,j]&lt;br /&gt;
                neighbour=self.lattice[(i+1)%self.n_rows,j]+self.lattice[i,(j+1)%self.n_cols]+self.lattice[(i-1)%self.n_rows,j]+self.lattice[i,(j-1)%self.n_cols]&lt;br /&gt;
                energy += -neighbour*s&lt;br /&gt;
        &lt;br /&gt;
        return energy/2&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK5&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_VERIFY.png|thumb|center|50000px|&#039;&#039;&#039;Figure 1.&#039;&#039;&#039; Test result from ILcheck.py. Expected values matches with actual values showing the code is correct.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK6&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100&amp;lt;/sup&amp;gt;=1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt; configurations. Time taken is 1.268*10&amp;lt;sup&amp;gt;30&amp;lt;/sup&amp;gt;/10&amp;lt;sup&amp;gt;9&amp;lt;/sup&amp;gt; = 1.268*10&amp;lt;sup&amp;gt;21&amp;lt;/sup&amp;gt; seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK7&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        self.E+=self.energy()&lt;br /&gt;
        self.E2+=self.energy()**2&lt;br /&gt;
        self.M+=self.magnetisation()&lt;br /&gt;
        self.M2+=self.magnetisation()**2        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/self.n_cycles, self.E2/self.n_cycles, self.M/self.n_cycles, self.M2/self.n_cycles, self.n_cycles&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK8&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When T&amp;lt;T&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;, energetic driving force dominates so spins tend to become parallel and energy will tend to the lowest possible value. A spontaneous magnetisation is expected. &amp;lt;M&amp;gt; is expected to be non-zero.&lt;br /&gt;
[[File:JP3915_allblack.png|thumb|center|500px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; System reaching the equilibrium where all the spins are parallel. After 1260 Monte Carlo steps &amp;lt;E&amp;gt;=-113.987J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=13666.794J, &amp;lt;M&amp;gt;=-56.221A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=3373.089A/m. After 15784 Monte Carlo steps &amp;lt;E&amp;gt;=-126.881J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=16167.092J, &amp;lt;M&amp;gt;=-63.379A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=4038.292A/m. As it can be seen from these two set of data that energy is converging to the lowest possible value, which is -NDJ. Magnetisation is also converging to the lowest possible value, which in this case is -64 (all spin down).]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_half.png|thumb|center|500px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Phase transition at Curie temperature. At this state, &amp;lt;E&amp;gt;=-96.000J, &amp;lt;E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=9216.000J, &amp;lt;M&amp;gt;=0.000A/m, &amp;lt;M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;gt;=0.000A/m. There are same numbers of spin up and spin down that cancel out and give 0 magnetisation.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK9&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|8.492&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|8.525&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|8.566&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|8.677&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|8.581&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|8.431&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|8.458&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|8.597&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|8.660&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|8.692&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 8.568±0.295 seconds.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK10&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&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=0&lt;br /&gt;
        energy=-np.sum(np.multiply(self.lattice,np.roll(self.lattice, 1, axis=1))+np.multiply(self.lattice,np.roll(self.lattice, 1, axis=0)))  &lt;br /&gt;
        return energy&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)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK11&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039; version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Number of run&lt;br /&gt;
!Time taken/s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0.394&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0.389&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0.390&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0.393&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0.387&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0.393&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The average value of times taken is calculated to be 0.391±0.047 seconds, which is much faster than the old version.&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK12&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915t0.5s8.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    def montecarlostep(self, T):&lt;br /&gt;
        # complete this function so that it performs a single Monte Carlo step&lt;br /&gt;
        E0 = self.energy()&lt;br /&gt;
        #the following two lines will select the coordinates of the random spin for you&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;
        if self.lattice[random_i][random_j]==1:&lt;br /&gt;
            self.lattice[random_i][random_j]=-1&lt;br /&gt;
        else:&lt;br /&gt;
            self.lattice[random_i][random_j]=1&lt;br /&gt;
        E1=self.energy()&lt;br /&gt;
        dE=E1-E0&lt;br /&gt;
        if dE&amp;lt;=0:&lt;br /&gt;
            self.lattice[random_i][random_j] = 1*self.lattice[random_i][random_j]&lt;br /&gt;
        elif dE&amp;gt;0:&lt;br /&gt;
            R=np.random.random()&lt;br /&gt;
            if R&amp;lt;= math.exp(-dE/T):&lt;br /&gt;
                self.lattice[random_i][random_j]=1*self.lattice[random_i][random_j]&lt;br /&gt;
            else:&lt;br /&gt;
                self.lattice[random_i][random_j]=-1*self.lattice[random_i][random_j]&lt;br /&gt;
        self.n_cycles+=1&lt;br /&gt;
        &lt;br /&gt;
        if self.n_cycles&amp;gt;self.n_rows*self.n_cols*200:&lt;br /&gt;
            self.E+=self.energy()&lt;br /&gt;
            self.E2+=self.energy()**2&lt;br /&gt;
            self.M+=self.magnetisation()&lt;br /&gt;
            self.M2+=self.magnetisation()**2&lt;br /&gt;
        else:&lt;br /&gt;
            pass&lt;br /&gt;
        &lt;br /&gt;
        return self.energy(), self.magnetisation()&lt;br /&gt;
    &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 returns them&lt;br /&gt;
        return self.E/(self.n_cycles-self.n_rows*self.n_cols*200), self.E2/(self.n_cycles-self.n_rows*self.n_cols*200), self.M/(self.n_cycles-self.n_rows*self.n_cols*200), self.M2/(self.n_cycles-self.n_rows*self.n_cols*200), self.n_cycles-self.n_rows*self.n_cols*200&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK13&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your initution and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. T NumPy function savetxt() stores your array of output data on disk &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8em.png|thumb|center|700px|&#039;&#039;&#039;Figure 2.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_8emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 3.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 8x8 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK14&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &#039;&#039;per spin&#039;&#039; 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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def plot_e_m(x):&lt;br /&gt;
    data= np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]/n&lt;br /&gt;
    e2=data[:,2]/n**2&lt;br /&gt;
    m=data[:,3]/n&lt;br /&gt;
    m2=data[:,4]/n**2&lt;br /&gt;
    eerr=(e2-e**2)**0.5&lt;br /&gt;
    merr=(m2-m**2)**0.5&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
plot_e_m(&#039;2x2_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def enermagne(x,y,z):&lt;br /&gt;
    datax = np.loadtxt(x)&lt;br /&gt;
    datay = np.loadtxt(y)&lt;br /&gt;
    dataz = np.loadtxt(z)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=datax[:,0]&lt;br /&gt;
    e=(datax[:,1]+datay[:,1]+dataz[:,1])/(n*3)&lt;br /&gt;
    m=(datax[:,3]+datay[:,3]+dataz[:,3])/(n*3)&lt;br /&gt;
    E=[datax[:,1]/n,datay[:,1]/n,dataz[:,1]/n]&lt;br /&gt;
    M=[datax[:,3]/n,datay[:,3]/n,dataz[:,3]/n]&lt;br /&gt;
    eerr=np.std(E, axis = 0)&lt;br /&gt;
    merr=np.std(M, axis = 0)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    errorbar(t, e,  yerr=eerr,color=&#039;orange&#039;)&lt;br /&gt;
    errorbar(t, m,  yerr=merr,color=&#039;green&#039;)&lt;br /&gt;
    plot(t, e, color=&#039;orange&#039;,marker=&#039;o&#039;,label=&#039;energy&#039;)&lt;br /&gt;
    plot(t,m,color=&#039;green&#039;,marker=&#039;o&#039;,label=&#039;magnetisation&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    title(&#039;The effect of temperature using &#039;+ns+&#039;x&#039;+ns+&#039; lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
enermagne(&#039;32x32_new.dat&#039;,&#039;32x32_newone.dat&#039;,&#039;32x32_repeat1.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2em.png|thumb|center|700px|&#039;&#039;&#039;Figure 4.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_2emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 5.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 2x2 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4em.png|thumb|center|700px|&#039;&#039;&#039;Figure 6.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_4emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 7.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 4x4 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16em.png|thumb|center|700px|&#039;&#039;&#039;Figure 8.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_16emavg.png|thumb|center|700px|&#039;&#039;&#039;Figure 9.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 16x16 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32em.png|thumb|center|700px|&#039;&#039;&#039;Figure 10.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice.]]&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_32emavgg.png|thumb|center|700px|&#039;&#039;&#039;Figure 11.&#039;&#039;&#039; Energy and magnetisation per spin vs temperature of a 32x32 lattice of 3 repeats.]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK15&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK16&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; 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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def heatcapa(x):&lt;br /&gt;
    data=np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    t=data[:,0]&lt;br /&gt;
    e=data[:,1]&lt;br /&gt;
    e2=data[:,2]&lt;br /&gt;
    c=(e2-(e**2))/((t**2)*n)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(t, c, color=&#039;orange&#039;,marker=&#039;o&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    title(&#039;Heat Capacity versus Temperature of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
    return&lt;br /&gt;
heatcapa(&#039;32x32_new.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:JP3915_cown.png|thumb|center|1000px|&#039;&#039;&#039;Figure 12.&#039;&#039;&#039; Heat Capacity vs Temperature of different lattice size.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK17&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (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 &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; 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 (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK18&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def polyfit(x):&lt;br /&gt;
    data = np.loadtxt(x) #assume data is now a 2D array containing two columns, T and C&lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = data[:,5] # get the second column&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])&lt;br /&gt;
    n=l**2&lt;br /&gt;
    fit = np.polyfit(T, C, 101) # fit a third order polynomial&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;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by C++&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    ns=str(l)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by C++ of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()&lt;br /&gt;
polyfit(&#039;8x8c.dat&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK19&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
def ownpolyfit(x):&lt;br /&gt;
    data = np.loadtxt(x)&lt;br /&gt;
    l=int(x.split(&#039;x&#039;)[0])    &lt;br /&gt;
    T = data[:,0] #get the first column&lt;br /&gt;
    C = (data[:,2]-(data[:,1])**2)/(T**2*(l**2)) # get the second column&lt;br /&gt;
    Tmin = 2 #for example&lt;br /&gt;
    Tmax = 2.8 #for example&lt;br /&gt;
    selection = np.logical_and(T &amp;gt; Tmin, T &amp;lt; Tmax) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T_values = T[selection]&lt;br /&gt;
    peak_C_values = C[selection]&lt;br /&gt;
    fit = np.polyfit(peak_T_values, peak_C_values, 7) # fit a third order polynomial&lt;br /&gt;
    T_min = np.min(peak_T_values)&lt;br /&gt;
    T_max = np.max(peak_T_values)&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;
    ns=str(l)&lt;br /&gt;
    figure(figsize=[18,12])&lt;br /&gt;
    plot(T, C, marker=&#039;o&#039;,label=&#039;simulation by Python&#039;)&lt;br /&gt;
    plot(T_range, fitted_C_values, marker=&#039;o&#039;, label=&#039;polynomial fitting&#039;)&lt;br /&gt;
    xlabel(&#039;Tempurature/K&#039;)&lt;br /&gt;
    ylabel(&#039;Heat Capacity/J/K&#039;)&lt;br /&gt;
    legend()&lt;br /&gt;
    title(&#039;polynomial fitting versus simulation by Python of &#039;+ns+&#039;x&#039;+ns+&#039; Lattice&#039;)&lt;br /&gt;
    show()    &lt;br /&gt;
    Cmax = np.max(fitted_C_values)&lt;br /&gt;
    Tmax = T_range[fitted_C_values == Cmax]&lt;br /&gt;
    return (Cmax, Tmax)&lt;br /&gt;
ownpolyfit(&amp;quot;16x16_new.dat&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK20&amp;lt;/big&amp;gt;&#039;&#039;&#039;==&lt;br /&gt;
&#039;&#039;&#039;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 &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. 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.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915t4s8.png&amp;diff=641616</id>
		<title>File:JP3915t4s8.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915t4s8.png&amp;diff=641616"/>
		<updated>2017-11-19T15:50:23Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915t2s10.png&amp;diff=641615</id>
		<title>File:JP3915t2s10.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915t2s10.png&amp;diff=641615"/>
		<updated>2017-11-19T15:50:09Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915t1s20.png&amp;diff=641614</id>
		<title>File:JP3915t1s20.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915t1s20.png&amp;diff=641614"/>
		<updated>2017-11-19T15:49:57Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915t1s10v2.png&amp;diff=641612</id>
		<title>File:JP3915t1s10v2.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915t1s10v2.png&amp;diff=641612"/>
		<updated>2017-11-19T15:49:41Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915t1s8.png&amp;diff=641611</id>
		<title>File:JP3915t1s8.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915t1s8.png&amp;diff=641611"/>
		<updated>2017-11-19T15:49:26Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915t1s8v3.png&amp;diff=641608</id>
		<title>File:JP3915t1s8v3.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915t1s8v3.png&amp;diff=641608"/>
		<updated>2017-11-19T15:49:05Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915t1.5s8v2.png&amp;diff=641607</id>
		<title>File:JP3915t1.5s8v2.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:JP3915t1.5s8v2.png&amp;diff=641607"/>
		<updated>2017-11-19T15:48:47Z</updated>

		<summary type="html">&lt;p&gt;Jp3915: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Jp3915</name></author>
	</entry>
</feed>