<?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=Im915</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=Im915"/>
	<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/wiki/Special:Contributions/Im915"/>
	<updated>2026-04-04T18:42:10Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.0</generator>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735978</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735978"/>
		<updated>2018-11-19T16:05:44Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Finding the peak in C */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment. While all spins aligned is more energetically favourable, this is the minimum entropy state, and the balance between entropy maximisation and energy minimisation leads to a phase transition.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
    magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check script&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \left ( SEM = {\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less than a certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left [ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right ]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) + \frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Taking each term in turn, starting with the first term;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) = \frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial Z}{\partial \beta} = - \sum E_i exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) =  \sum E_i ^2 exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) = \frac{\sum E_i ^2 exp \left\{ -\beta E_i \right \}}{\sum exp \left\{-E_i \beta\right\}} = \left\langle E^2\right\rangle&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now taking the second term; &amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right ) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt; by the product rule. Therefore;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}\frac{\partial Z}{\partial \beta} = \frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2 = \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As seen before,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Combining &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;, gives&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\left\langle E^2\right\rangle}{k_b T^2} - \frac{\left\langle E\right\rangle ^ 2}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As &amp;lt;math&amp;gt; \mathrm{Var}[E] = \left\langle E^2\right\rangle - \left\langle E\right\rangle ^ 2 &amp;lt;/math&amp;gt;, Variance can be substituted into this equation to give the desired result;&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;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T&amp;lt;sub&amp;gt;C&amp;lt;/sub&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes_full.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:Curie_for_diff_lattice_sizes_full.txt&amp;diff=735977</id>
		<title>File:Curie for diff lattice sizes full.txt</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:Curie_for_diff_lattice_sizes_full.txt&amp;diff=735977"/>
		<updated>2018-11-19T16:04:32Z</updated>

		<summary type="html">&lt;p&gt;Im915: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:Curie_for_diff_lattice_sizes.txt&amp;diff=735976</id>
		<title>File:Curie for diff lattice sizes.txt</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:Curie_for_diff_lattice_sizes.txt&amp;diff=735976"/>
		<updated>2018-11-19T16:03:21Z</updated>

		<summary type="html">&lt;p&gt;Im915: Im915 uploaded a new version of File:Curie for diff lattice sizes.txt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735975</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735975"/>
		<updated>2018-11-19T15:58:07Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Finding the peak in C */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment. While all spins aligned is more energetically favourable, this is the minimum entropy state, and the balance between entropy maximisation and energy minimisation leads to a phase transition.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
    magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check script&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \left ( SEM = {\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less than a certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left [ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right ]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) + \frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Taking each term in turn, starting with the first term;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) = \frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial Z}{\partial \beta} = - \sum E_i exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) =  \sum E_i ^2 exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) = \frac{\sum E_i ^2 exp \left\{ -\beta E_i \right \}}{\sum exp \left\{-E_i \beta\right\}} = \left\langle E^2\right\rangle&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now taking the second term; &amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right ) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt; by the product rule. Therefore;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}\frac{\partial Z}{\partial \beta} = \frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2 = \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As seen before,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Combining &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;, gives&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\left\langle E^2\right\rangle}{k_b T^2} - \frac{\left\langle E\right\rangle ^ 2}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As &amp;lt;math&amp;gt; \mathrm{Var}[E] = \left\langle E^2\right\rangle - \left\langle E\right\rangle ^ 2 &amp;lt;/math&amp;gt;, Variance can be substituted into this equation to give the desired result;&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;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T&amp;lt;sub&amp;gt;C&amp;lt;/sub&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735974</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735974"/>
		<updated>2018-11-19T15:57:41Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Finding the peak in C */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment. While all spins aligned is more energetically favourable, this is the minimum entropy state, and the balance between entropy maximisation and energy minimisation leads to a phase transition.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
    magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check script&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \left ( SEM = {\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less than a certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left [ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right ]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) + \frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Taking each term in turn, starting with the first term;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) = \frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial Z}{\partial \beta} = - \sum E_i exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) =  \sum E_i ^2 exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) = \frac{\sum E_i ^2 exp \left\{ -\beta E_i \right \}}{\sum exp \left\{-E_i \beta\right\}} = \left\langle E^2\right\rangle&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now taking the second term; &amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right ) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt; by the product rule. Therefore;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}\frac{\partial Z}{\partial \beta} = \frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2 = \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As seen before,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Combining &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;, gives&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\left\langle E^2\right\rangle}{k_b T^2} - \frac{\left\langle E\right\rangle ^ 2}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As &amp;lt;math&amp;gt; \mathrm{Var}[E] = \left\langle E^2\right\rangle - \left\langle E\right\rangle ^ 2 &amp;lt;/math&amp;gt;, Variance can be substituted into this equation to give the desired result;&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;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735938</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735938"/>
		<updated>2018-11-19T11:55:09Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* The effect of temperature */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment. While all spins aligned is more energetically favourable, this is the minimum entropy state, and the balance between entropy maximisation and energy minimisation leads to a phase transition.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
    magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check script&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \left ( SEM = {\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less than a certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left [ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right ]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) + \frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Taking each term in turn, starting with the first term;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) = \frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial Z}{\partial \beta} = - \sum E_i exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) =  \sum E_i ^2 exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) = \frac{\sum E_i ^2 exp \left\{ -\beta E_i \right \}}{\sum exp \left\{-E_i \beta\right\}} = \left\langle E^2\right\rangle&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now taking the second term; &amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right ) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt; by the product rule. Therefore;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}\frac{\partial Z}{\partial \beta} = \frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2 = \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As seen before,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Combining &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;, gives&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\left\langle E^2\right\rangle}{k_b T^2} - \frac{\left\langle E\right\rangle ^ 2}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As &amp;lt;math&amp;gt; \mathrm{Var}[E] = \left\langle E^2\right\rangle - \left\langle E\right\rangle ^ 2 &amp;lt;/math&amp;gt;, Variance can be substituted into this equation to give the desired result;&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;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735934</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735934"/>
		<updated>2018-11-19T11:51:01Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Accelerating the Code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment. While all spins aligned is more energetically favourable, this is the minimum entropy state, and the balance between entropy maximisation and energy minimisation leads to a phase transition.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
    magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check script&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \left ( SEM = {\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left [ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right ]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) + \frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Taking each term in turn, starting with the first term;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) = \frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial Z}{\partial \beta} = - \sum E_i exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) =  \sum E_i ^2 exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) = \frac{\sum E_i ^2 exp \left\{ -\beta E_i \right \}}{\sum exp \left\{-E_i \beta\right\}} = \left\langle E^2\right\rangle&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now taking the second term; &amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right ) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt; by the product rule. Therefore;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}\frac{\partial Z}{\partial \beta} = \frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2 = \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As seen before,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Combining &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;, gives&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\left\langle E^2\right\rangle}{k_b T^2} - \frac{\left\langle E\right\rangle ^ 2}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As &amp;lt;math&amp;gt; \mathrm{Var}[E] = \left\langle E^2\right\rangle - \left\langle E\right\rangle ^ 2 &amp;lt;/math&amp;gt;, Variance can be substituted into this equation to give the desired result;&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;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735921</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735921"/>
		<updated>2018-11-19T11:42:27Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Importance Sampling */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment. While all spins aligned is more energetically favourable, this is the minimum entropy state, and the balance between entropy maximisation and energy minimisation leads to a phase transition.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
    magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check script&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left [ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right ]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) + \frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Taking each term in turn, starting with the first term;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) = \frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial Z}{\partial \beta} = - \sum E_i exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) =  \sum E_i ^2 exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) = \frac{\sum E_i ^2 exp \left\{ -\beta E_i \right \}}{\sum exp \left\{-E_i \beta\right\}} = \left\langle E^2\right\rangle&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now taking the second term; &amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right ) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt; by the product rule. Therefore;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}\frac{\partial Z}{\partial \beta} = \frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2 = \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As seen before,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Combining &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;, gives&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\left\langle E^2\right\rangle}{k_b T^2} - \frac{\left\langle E\right\rangle ^ 2}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As &amp;lt;math&amp;gt; \mathrm{Var}[E] = \left\langle E^2\right\rangle - \left\langle E\right\rangle ^ 2 &amp;lt;/math&amp;gt;, Variance can be substituted into this equation to give the desired result;&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;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735919</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735919"/>
		<updated>2018-11-19T11:41:17Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Testing the files */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment. While all spins aligned is more energetically favourable, this is the minimum entropy state, and the balance between entropy maximisation and energy minimisation leads to a phase transition.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
    magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check script&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left [ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right ]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) + \frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Taking each term in turn, starting with the first term;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) = \frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial Z}{\partial \beta} = - \sum E_i exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) =  \sum E_i ^2 exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) = \frac{\sum E_i ^2 exp \left\{ -\beta E_i \right \}}{\sum exp \left\{-E_i \beta\right\}} = \left\langle E^2\right\rangle&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now taking the second term; &amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right ) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt; by the product rule. Therefore;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}\frac{\partial Z}{\partial \beta} = \frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2 = \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As seen before,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Combining &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;, gives&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\left\langle E^2\right\rangle}{k_b T^2} - \frac{\left\langle E\right\rangle ^ 2}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As &amp;lt;math&amp;gt; \mathrm{Var}[E] = \left\langle E^2\right\rangle - \left\langle E\right\rangle ^ 2 &amp;lt;/math&amp;gt;, Variance can be substituted into this equation to give the desired result;&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;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735917</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735917"/>
		<updated>2018-11-19T11:40:20Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Modifying the files */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment. While all spins aligned is more energetically favourable, this is the minimum entropy state, and the balance between entropy maximisation and energy minimisation leads to a phase transition.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;def magnetisation(self):&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
    magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left [ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right ]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) + \frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Taking each term in turn, starting with the first term;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) = \frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial Z}{\partial \beta} = - \sum E_i exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) =  \sum E_i ^2 exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) = \frac{\sum E_i ^2 exp \left\{ -\beta E_i \right \}}{\sum exp \left\{-E_i \beta\right\}} = \left\langle E^2\right\rangle&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now taking the second term; &amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right ) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt; by the product rule. Therefore;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}\frac{\partial Z}{\partial \beta} = \frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2 = \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As seen before,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Combining &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;, gives&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\left\langle E^2\right\rangle}{k_b T^2} - \frac{\left\langle E\right\rangle ^ 2}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As &amp;lt;math&amp;gt; \mathrm{Var}[E] = \left\langle E^2\right\rangle - \left\langle E\right\rangle ^ 2 &amp;lt;/math&amp;gt;, Variance can be substituted into this equation to give the desired result;&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;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735916</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735916"/>
		<updated>2018-11-19T11:39:57Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Calculating the energy and magnetisation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment. While all spins aligned is more energetically favourable, this is the minimum entropy state, and the balance between entropy maximisation and energy minimisation leads to a phase transition.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
      magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left [ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right ]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) + \frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Taking each term in turn, starting with the first term;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) = \frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial Z}{\partial \beta} = - \sum E_i exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) =  \sum E_i ^2 exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) = \frac{\sum E_i ^2 exp \left\{ -\beta E_i \right \}}{\sum exp \left\{-E_i \beta\right\}} = \left\langle E^2\right\rangle&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now taking the second term; &amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right ) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt; by the product rule. Therefore;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}\frac{\partial Z}{\partial \beta} = \frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2 = \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As seen before,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Combining &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;, gives&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\left\langle E^2\right\rangle}{k_b T^2} - \frac{\left\langle E\right\rangle ^ 2}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As &amp;lt;math&amp;gt; \mathrm{Var}[E] = \left\langle E^2\right\rangle - \left\langle E\right\rangle ^ 2 &amp;lt;/math&amp;gt;, Variance can be substituted into this equation to give the desired result;&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;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735914</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735914"/>
		<updated>2018-11-19T11:39:36Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Modifying the files */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment. While all spins aligned is more energetically favourable, this is the minimum entropy state, and the balance between entropy maximisation and energy minimisation leads to a phase transition.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
      magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left [ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right ]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) + \frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Taking each term in turn, starting with the first term;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) = \frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial Z}{\partial \beta} = - \sum E_i exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) =  \sum E_i ^2 exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) = \frac{\sum E_i ^2 exp \left\{ -\beta E_i \right \}}{\sum exp \left\{-E_i \beta\right\}} = \left\langle E^2\right\rangle&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now taking the second term; &amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right ) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt; by the product rule. Therefore;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}\frac{\partial Z}{\partial \beta} = \frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2 = \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As seen before,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Combining &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;, gives&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\left\langle E^2\right\rangle}{k_b T^2} - \frac{\left\langle E\right\rangle ^ 2}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As &amp;lt;math&amp;gt; \mathrm{Var}[E] = \left\langle E^2\right\rangle - \left\langle E\right\rangle ^ 2 &amp;lt;/math&amp;gt;, Variance can be substituted into this equation to give the desired result;&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;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735909</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735909"/>
		<updated>2018-11-19T11:31:58Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* The Model */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment. While all spins aligned is more energetically favourable, this is the minimum entropy state, and the balance between entropy maximisation and energy minimisation leads to a phase transition.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left [ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right ]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) + \frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Taking each term in turn, starting with the first term;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) = \frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial Z}{\partial \beta} = - \sum E_i exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) =  \sum E_i ^2 exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) = \frac{\sum E_i ^2 exp \left\{ -\beta E_i \right \}}{\sum exp \left\{-E_i \beta\right\}} = \left\langle E^2\right\rangle&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now taking the second term; &amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right ) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt; by the product rule. Therefore;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}\frac{\partial Z}{\partial \beta} = \frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2 = \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As seen before,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Combining &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;, gives&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\left\langle E^2\right\rangle}{k_b T^2} - \frac{\left\langle E\right\rangle ^ 2}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As &amp;lt;math&amp;gt; \mathrm{Var}[E] = \left\langle E^2\right\rangle - \left\langle E\right\rangle ^ 2 &amp;lt;/math&amp;gt;, Variance can be substituted into this equation to give the desired result;&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;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735846</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735846"/>
		<updated>2018-11-18T22:10:05Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left [ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right ]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) + \frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Taking each term in turn, starting with the first term;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) = \frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial Z}{\partial \beta} = - \sum E_i exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) =  \sum E_i ^2 exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) = \frac{\sum E_i ^2 exp \left\{ -\beta E_i \right \}}{\sum exp \left\{-E_i \beta\right\}} = \left\langle E^2\right\rangle&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now taking the second term; &amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right ) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt; by the product rule. Therefore;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}\frac{\partial Z}{\partial \beta} = \frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2 = \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As seen before,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Combining &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;, gives&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\left\langle E^2\right\rangle}{k_b T^2} - \frac{\left\langle E\right\rangle ^ 2}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As &amp;lt;math&amp;gt; \mathrm{Var}[E] = \left\langle E^2\right\rangle - \left\langle E\right\rangle ^ 2 &amp;lt;/math&amp;gt;, Variance can be substituted into this equation to give the desired result;&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;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735845</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735845"/>
		<updated>2018-11-18T22:07:21Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left [ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right ]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) + \frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Taking each term in turn, starting with the first term;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) = \frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial Z}{\partial \beta} = - \sum E_i exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) =  \sum E_i ^2 exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) = \frac{\sum E_i ^2 exp \left\{ -\beta E_i \right \}}{\sum exp \left\{-E_i \beta\right\}} = \left\langle E^2\right\rangle&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now taking the second term; &amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right ) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt; by the product rule. Therefore;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}\frac{\partial Z}{\partial \beta} = \frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2 = \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As seen before,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Combining &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\left\langle E^2\right\rangle}{k_b T^2} - \frac{\left\langle E\right\rangle ^ 2}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735844</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735844"/>
		<updated>2018-11-18T22:04:10Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left [ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right ]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) + \frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Taking each term in turn, starting with the first term;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) = \frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial Z}{\partial \beta} = - \sum E_i exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) =  \sum E_i ^2 exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) = \frac{\sum E_i ^2 exp \left\{ -\beta E_i \right \}}{\sum exp \left\{-E_i \beta\right\}} = \left\langle E^2\right\rangle&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now taking the second term; &amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right ) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt; by the product rule. Therefore;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}\frac{\partial Z}{\partial \beta} = \frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2 = \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = - \left\langle E^2\right\rangle + \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735842</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735842"/>
		<updated>2018-11-18T22:02:39Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left [ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right ]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) + \frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Taking each term in turn, starting with the first term;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) = \frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial Z}{\partial \beta} = - \sum E_i exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) =  \sum E_i ^2 exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) = \frac{\sum E_i ^2 exp \left\{ -\beta E_i \right \}}{\sum exp \left\{-E_i \beta\right\}} = \left\langle E^2\right\rangle&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now taking the second term; &amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right ) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt; by the product rule. Therefore;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}\frac{\partial Z}{\partial \beta} = \frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2 = \left\langle E\right\rangle ^ 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735839</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735839"/>
		<updated>2018-11-18T22:01:25Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left [ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right ]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) + \frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Taking each term in turn, starting with the first term;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) = \frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial Z}{\partial \beta} = - \sum E_i exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) =  \sum E_i ^2 exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) = \frac{\sum E_i ^2 exp \left\{ -\beta E_i \right \}}{\sum exp \left\{-E_i \beta\right\}} = \left\langle E^2\right\rangle&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now taking the second term; &amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right ) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt; by the product rule. Therefore;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right) = \frac{1}{Z^2}\frac{\partial Z}{\partial \beta}\frac{\partial Z}{\partial \beta} = \frac{1}{Z^2} \left ( \frac{\partial Z}{\partial \beta} \right )^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735835</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735835"/>
		<updated>2018-11-18T21:57:22Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left [ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right ]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) + \frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Taking each term in turn, starting with the first term;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) = \frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial Z}{\partial \beta} = - \sum E_i exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) =  \sum E_i ^2 exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) = \frac{\sum E_i ^2 exp \left\{ -\beta E_i \right \}}{\sum exp \left\{-E_i \beta\right\}} = \left\langle E^2\right\rangle&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now taking the second term;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735833</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735833"/>
		<updated>2018-11-18T21:55:05Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left [ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right ]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) + \frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Taking each term in turn;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) = \frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial Z}{\partial \beta} = - \sum E_i exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) =  \sum E_i ^2 exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) = \frac{\sum E_i ^2 exp \left\{ -\beta E_i \right \}}{\sum exp \left\{-E_i \beta\right\}} = \left\langle E^2\right\rangle&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735832</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735832"/>
		<updated>2018-11-18T21:54:45Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left [ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right ]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) + \frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Taking each term in turn;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) = \frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial Z}{\partial \beta} = - \sum E_i exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) =  \sum E_i ^2 exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) = \frac{sum E_i ^2 exp \left\{ -\beta E_i \right \}}{\sum exp \left\{-E_i \beta\right\}} = \left\langle E^2\right\rangle&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735828</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735828"/>
		<updated>2018-11-18T21:51:31Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left [ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right ]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) + \frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Taking each term in turn;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) = \frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial Z}{\partial \beta} = - \sum E_i exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) =  \sum E_i ^2 exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735825</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735825"/>
		<updated>2018-11-18T21:50:15Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left [ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right ]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) + \frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Taking each term in turn;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) = \frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial Z}{\partial \beta} = - \sum E_i exp \left\{ -\beta E_i \right \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735820</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735820"/>
		<updated>2018-11-18T21:46:54Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left [ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right ]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) + \frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Taking each term in turn;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) = \frac{-1}{Z} \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735819</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735819"/>
		<updated>2018-11-18T21:44:05Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left [ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right ]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left ( \frac{\partial Z}{\partial \beta} \right ) \times \left ( \frac{-1}{Z} \right ) + \frac{\partial}{\partial \beta} \left ( \frac{-1}{Z} \right )\times \left ( \frac{\partial Z}{\partial \beta} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735815</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735815"/>
		<updated>2018-11-18T21:39:43Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left [ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right ]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735813</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735813"/>
		<updated>2018-11-18T21:38:39Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left \{ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735811</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735811"/>
		<updated>2018-11-18T21:38:17Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left \( -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right\)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735809</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735809"/>
		<updated>2018-11-18T21:37:50Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left \{ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735807</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735807"/>
		<updated>2018-11-18T21:36:40Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} \left \[ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right\]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735806</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735806"/>
		<updated>2018-11-18T21:35:41Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta} [ -\frac{1}{z} \frac{\partial Z}{\partial \beta}]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735805</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735805"/>
		<updated>2018-11-18T21:35:13Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta} = \frac{\partial}{\partial \beta}\left\[ -\frac{1}{z} \frac{\partial Z}{\partial \beta}\right\]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735804</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735804"/>
		<updated>2018-11-18T21:31:49Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  -\frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735803</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735803"/>
		<updated>2018-11-18T21:31:22Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It can be seen that &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z} \frac{\partial Z}{\partial \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735802</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735802"/>
		<updated>2018-11-18T21:29:12Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735801</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735801"/>
		<updated>2018-11-18T21:28:25Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp exp \left\{-E_i \beta\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735800</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735800"/>
		<updated>2018-11-18T21:27:40Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E_i exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735799</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735799"/>
		<updated>2018-11-18T21:27:18Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E\left(\alpha\right) exp \left\{-E_i \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735798</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735798"/>
		<updated>2018-11-18T21:26:28Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E\left(\alpha\right) exp \left\{-E\left(\alpha\right) \beta\right\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735797</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735797"/>
		<updated>2018-11-18T21:25:45Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E\left(\alpha\right) exp -E\left(\alpha\right) \beta&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735796</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735796"/>
		<updated>2018-11-18T21:24:49Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp {\left\-E\left(\alpha\right) \beta}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735795</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735795"/>
		<updated>2018-11-18T21:23:57Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-E\left(\alpha\right) \beta &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735794</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735794"/>
		<updated>2018-11-18T21:22:57Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, &amp;lt;math&amp;gt; Z = \sum exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735793</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735793"/>
		<updated>2018-11-18T21:21:01Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As given before, &amp;lt;math&amp;gt;\left\langle E\right\rangle =  \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735792</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735792"/>
		<updated>2018-11-18T21:18:29Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial \beta}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735791</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735791"/>
		<updated>2018-11-18T21:18:12Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leaving aside &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T}&amp;lt;/math&amp;gt; for now, we can analyse \frac{\partial \left\langle E\right\rangle}{\partial \beta}.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735790</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735790"/>
		<updated>2018-11-18T21:17:17Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735789</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735789"/>
		<updated>2018-11-18T21:17:07Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;&lt;br /&gt;
Therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735788</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735788"/>
		<updated>2018-11-18T21:16:51Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;; therefore &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735787</id>
		<title>Rep:CMP3:IM915</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:CMP3:IM915&amp;diff=735787"/>
		<updated>2018-11-18T21:16:22Z</updated>

		<summary type="html">&lt;p&gt;Im915: /* Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction to the Ising Model==&lt;br /&gt;
&lt;br /&gt;
===The Model===&lt;br /&gt;
&lt;br /&gt;
The Ising model is a physical model used to describe and understand ferromagnets- materials where there is preferential alignment of magnetic dipoles along certain directions and therefore an overall magnetic moment.&lt;br /&gt;
&lt;br /&gt;
In the Ising model, a collection of spins is considered arranged on a lattice (a line in 1D, a grid in 2D, a cuboid in 3D). When no magnetic field is applied, the only contribution to energy is from the interaction of adjacent spins. Periodic boundary conditions are applied. &lt;br /&gt;
&lt;br /&gt;
Total interaction energy is defined by the equation: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Where &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; is a constant which controls the strength of interaction and &amp;lt;math&amp;gt;&amp;lt;i,j&amp;gt;&amp;lt;/math&amp;gt; indicates adjacent spins.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 N is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The lowest energy state for the Ising model (with no applied field) will be all spins aligned (either all up or all down). &lt;br /&gt;
&lt;br /&gt;
Therefore, using the equation for total energy:&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
As all spins are aligned, &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s_i s_j = s_i ^2 = 1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of neighbours is related to dimensionality- in 1D each spin has 2 neighbours, in 2D each spin has 4, and in 3D each spin has 6 - i.e. there are 2D neighbours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = 2ND &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2ND&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Total energy &amp;lt;math&amp;gt; = -DNJ &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity of this state is 2, as there are two degenerate configurations with all spins aligned (all spins = +1, and all spins = -1).&lt;br /&gt;
&lt;br /&gt;
Entropy is calculated from multiplicity using the Boltzmann&#039;s Entropy Equation where W is multiplicity and k&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt; is the Boltzmann Constant:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln W &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S = k_b \ln 2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Phase Transitions===&lt;br /&gt;
&lt;br /&gt;
Provided a lattice of 2 or more dimensions is used, the Ising model displays a phase transition. The temperature controls the balance between energetics (favouring lowest energy configuration) and entropy (favouring the configuration with the maximum number of degenerate states).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 (D=3,\ N=1000)? How much entropy does the system gain by doing so?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a single spin is flipped, all its interactions with its neighbours are now unfavourable rather than favourable. Therefore for this one particular spin, &amp;lt;math&amp;gt; \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = -2D &amp;lt;/math&amp;gt; as &amp;lt;math&amp;gt;s_i s_j = -1 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Therefore, &amp;lt;math&amp;gt; \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j  = 2D(N-1)-2D = 2D(N-2) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total energy = &amp;lt;math&amp;gt; - \frac{1}{2} J \sum_i^N \sum_{&amp;lt;i,j&amp;gt;} s_i s_j = - \frac{1}{2} J \times 2D(N-2) = -JD(N-2) = -JND + 2JD &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Therefore, the total energy change of the system is equal to &amp;lt;math&amp;gt;+ 2JD&amp;lt;/math&amp;gt;, which is equal to 6J in this case (D=3).&lt;br /&gt;
&lt;br /&gt;
The entropy change of the system can be calculated by considering the change in number of configurations. The number of configurations of a system with 1000 spins where a single spin is flipped is 2000 (1000 with the single spin = +1, and 1000 with the single spin = -1). &lt;br /&gt;
&lt;br /&gt;
So the entropy of the new system is equal to  &amp;lt;math&amp;gt;S = k_b \ln {2000} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entropy change = &amp;lt;math&amp;gt;\Delta S = k_b \ln {2000} - k_b \ln 2 = 6.908 k_b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with D = 3,\ N=1000 at absolute zero?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total magnetisation of the system &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|300px|center|thumb|&#039;&#039;Illustration of an Ising lattice in one (N=5), two (N=5x5), and three (N=5x5x5) dimensions. Red cells indicate the &amp;quot;up&amp;quot; spin&amp;quot;, and blue cells indicate the &amp;quot;down&amp;quot; spin.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The magnetisation of the 1D lattice shown above is +1, and the 2D lattice also has a magnetisation of +1.&lt;br /&gt;
&lt;br /&gt;
At absolute zero, an Ising lattice will have all spins aligned (all either +1 or all either -1). The magnetisation I would expect for an Ising lattice with &amp;lt;math&amp;gt; D = 3 , N = 1000&amp;lt;/math&amp;gt; is &amp;lt;math&amp;gt; \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Calculating the energy and magnetisation==&lt;br /&gt;
&lt;br /&gt;
===Modifying the files===&lt;br /&gt;
&lt;br /&gt;
An IsingLattice class is defined, which contains the function &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt;, and the empty functions &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;statistics()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;IsingLattice(x,x)&amp;lt;/code&amp;gt; creates an IsingLattice object with x rows and x columns, with random spins. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The initially created functions for &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation&amp;lt;/code&amp;gt; are given below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Energy&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def energy(self):&lt;br /&gt;
&amp;quot;Return the total energy of the current lattice configuration. Assumes J = 1 at all times&amp;quot;&lt;br /&gt;
    for i in range(0,self.n_rows):&lt;br /&gt;
        for j in range(0,self.n_cols):&lt;br /&gt;
            pos_0 = self.lattice[i,j]&lt;br /&gt;
            pos_1 = self.lattice[i-1,j]&lt;br /&gt;
            pos_2 = self.lattice[i,j-1]&lt;br /&gt;
            pos_3 = self.lattice[i,(j+1)%self.n_cols]&lt;br /&gt;
            pos_4 = self.lattice[(i+1)%self.n_rows,j]&lt;br /&gt;
            energy_sum += pos_0*pos_1 + pos_0*pos_2 + pos_0*pos_3 + pos_0*pos_4&lt;br /&gt;
            energy = -energy_sum/2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Magnetisation&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt; def magnetisation(self):&lt;br /&gt;
          magnetisation = np.sum(self.lattice)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Testing the files===&lt;br /&gt;
&lt;br /&gt;
The provided &amp;lt;code&amp;gt;ILcheck.py&amp;lt;/code&amp;gt; script creates three IsingLattice objects and returns both the true energies and magnetisation, along with those generated by the previously written &amp;lt;code&amp;gt;energy()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;magnetisation()&amp;lt;/code&amp;gt; functions. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;%run ILcheck.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The check function shows that my energy and magnetisation functions work, as the expected and actual energy are equal. &lt;br /&gt;
&lt;br /&gt;
[[File:IL_Check_output_2_IM.png|500px|center|thumb|&#039;&#039;Output of the check function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
===Average Energy and Magnetisation===&lt;br /&gt;
&lt;br /&gt;
The statistical mechanics equations for average energy and magnetism, as functions of temperature, are shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle M\right\rangle_T = \frac{1}{Z}\sum_\alpha M\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\left\langle E\right\rangle_T = \frac{1}{Z}\sum_\alpha E\left(\alpha\right) \exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A system of 100 spins has a total of &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations. (Each spin has two possible orientations, and number of spins = 100). This is equal to &amp;lt;math&amp;gt;1.27\times 10^{30}&amp;lt;/math&amp;gt; configurations. If &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations can be analysed per second, to analyse every configuration will take &amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9}&amp;lt;/math&amp;gt; seconds. &lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1.27\times 10^{30}}{1\times 10^9} = 1.27\times 10^{21}&amp;lt;/math&amp;gt; seconds&lt;br /&gt;
&lt;br /&gt;
This is equal to &amp;lt;math&amp;gt;4.02 \times 10^{13}&amp;lt;/math&amp;gt; years, i.e. 4 thousand billion years.&lt;br /&gt;
&lt;br /&gt;
===Importance Sampling===&lt;br /&gt;
&lt;br /&gt;
In order to allow simulations to be run within reasonable time-frames, a smarter approach to analysing average energy and magnetism is required. The Bolzmann factor, &amp;lt;math&amp;gt;\exp \left\{-\frac{E\left(\alpha\right)}{k_BT}\right\}&amp;lt;/math&amp;gt;, is very small for the majority of configurations and these can therefore be ignored. By only considering the states with a relatively large Boltzmann factor, a simulation can be run within a reasonable time-frame.&lt;br /&gt;
&lt;br /&gt;
The Monte Carlo Method allows us to do this, by generating states randomly from the Boltzmann probability distribution. The algorithm is as follows.&lt;br /&gt;
&lt;br /&gt;
:1. Start from a given configuration of spins &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; with energy &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
:2. Choose a single spin at random and flip it, to give configuration &amp;lt;math&amp;gt;\alpha_1&amp;lt;/math&amp;gt;&lt;br /&gt;
:3. Calculate the new energy, &amp;lt;math&amp;gt;E_2&amp;lt;/math&amp;gt;&lt;br /&gt;
:4. Calculate the energy difference, &amp;lt;math&amp;gt;\Delta E = E_1 - E_0&amp;lt;/math&amp;gt;&lt;br /&gt;
::4.1 If &amp;lt;math&amp;gt;\Delta E &amp;lt; 0 &amp;lt;/math&amp;gt;  then the spin flipping decreased the energy and the new configuration is &amp;lt;b&amp;gt;accepted&amp;lt;/b&amp;gt;. Set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt;. Go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
::4.2 If &amp;lt;math&amp;gt;\Delta E &amp;gt; 0&amp;lt;/math&amp;gt;, spin flipping increased the energy and so the probability must be considered. The probability for the transition to occur is &amp;lt;math&amp;gt;\exp \left\{-\frac{\Delta E}{k_BT}\right\}&amp;lt;/math&amp;gt;. To ensure we select with the correct probability, we use the procedure;&lt;br /&gt;
:::&amp;lt;i&amp;gt;a)&amp;lt;/i&amp;gt; Choose a random number R in the interval [0,1)&lt;br /&gt;
:::&amp;lt;i&amp;gt;b)&amp;lt;/i&amp;gt; If &amp;lt;math&amp;gt;R \leq \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;accept&amp;lt;/b&amp;gt; the new configuration, set &amp;lt;math&amp;gt;\alpha_0 = \alpha_1&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0 = E_1&amp;lt;/math&amp;gt; and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;.&lt;br /&gt;
:::&amp;lt;i&amp;gt;c)&amp;lt;/i&amp;gt; If If &amp;lt;math&amp;gt;R &amp;gt; \exp \left\{-\frac{\Delta E}{k_BT}\right\} &amp;lt;/math&amp;gt;, we &amp;lt;b&amp;gt;reject&amp;lt;/b&amp;gt; the new configuration, leave &amp;lt;math&amp;gt;\alpha_0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;E_0&amp;lt;/math&amp;gt; unchanged and go to &amp;lt;b&amp;gt;step 5&amp;lt;/b&amp;gt;&lt;br /&gt;
:5. Update running averages of energy and magnetism&lt;br /&gt;
:6. Monte Carlo cycle complete. Return to &amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Modifying IsingLattice.py===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Montecarlo_IM_initial.PNG|1000px|thumb|center|&#039;&#039;Initial Monte Carlo code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Statistics_initial_function.PNG|1000px|thumb|center|&#039;&#039;Initial Statistics code&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, then spontaneous magnetisation is expected. T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; is the curie temperature, above which materials lose their permanent magnetic properties. At temperatures below T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the material will still have permanent magnetism. At temperatures above T&amp;lt;sub&amp;gt;c&amp;lt;/sub&amp;gt; the temperature is high enough that the increase in entropy by having random spin alignment is large enough to compensate for the fact random alignment is less energetically favorable.&lt;br /&gt;
&lt;br /&gt;
A simulation was run with an 8x8 lattice at T = 1 (below the Curie Temperature). &lt;br /&gt;
&lt;br /&gt;
[[File:Animation_final_frame.png|400 px|thumb|left|&#039;&#039;Final state of the system after running the animation to equilibrium&#039;&#039;]]&lt;br /&gt;
[[File:Animation_final_frame_energies.PNG|300 px|thumb|right|&#039;&#039;Averaged evergy and magnetism over this simulation&#039;&#039;]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Accelerating the Code==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your current 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILTimetrial.py&amp;lt;/code&amp;gt; was run 20 times and the times taken recorded. The average time to perform 2000 Monte Carlo steps was 4.82 seconds. Standard deviation was equal to 0.44 seconds, and therefore the standard error of the mean (SEM) was equal to 0.098 seconds. &lt;br /&gt;
&lt;br /&gt;
Standard Error of the Mean &amp;lt;math&amp;gt;{\sigma}_\bar{x}\ = \frac{\sigma}{\sqrt{n}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Look at the documentation for the 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 roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sum function was already being used to calculate &amp;lt;code&amp;gt;magnestisation()&amp;lt;/code&amp;gt; (code repeated again below) from the initial draft of the function, and so this function could not be improved.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;  magnetisation = np.sum(self.lattice)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The energy function however was inefficient, and was improved using roll and multiply. The modified energy function is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:New_energy_function_IM.PNG|1000px|thumb|center|&#039;&#039;New and more efficient energy function using roll and multiply functions&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use the script ILtimetrial.py to record how long your new 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!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ILtimetrial.py&amp;lt;/code&amp;gt; was run 20 times again on the modified code, and the times taken were recorded. The average time to perform 2000 Monte Carlo steps was 0.31 seconds, over 10 times faster than the initial code. Standard deviation was equal to 0.055 seconds, and therefore the standard error of the mean (SEM) was equal to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
When starting from a random configuration (as we do here) it takes a certain amount of Monte Carlo cycles to reach the equilibrium state, during which the energy rapidly drops and magnetisation rapidly changes. To calculate accurate averages of the system, these first steps should be disregarded. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of steps required for the system to reach equilibrium varies with lattice size and temperature. At smaller lattice sizes, the system reaches equilibrium much faster, which is always the lowest energy configuration. However at higher lattice sizes the system often gets stuck in meta-stable states where the system is not in the lowest energy configuration but is stuck in a local energy minimum, which is stable enough that a single spin flip is not enough to escape the well. At higher temperatures, the time taken to equilibrate is longer, and at high enough temperatures (above the curie temperature) the system equilibrates at points away from the minimum energy energy configuration, and new magnetisation decreases. Some images are shown below that demonstrate this.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:8x8_at_0.01K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 0.01K- takes ~300 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_1K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 1K- takes ~ 700 steps&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_at_2K.png|300px|thumb|center|&#039;&#039;8x8 Lattice at 2K- takes ~1000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_0.01K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 0.01K- takes ~3000 steps but gets stuck in a meta-stable state&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_at_1K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~4000 steps&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_at_2K.png|300px|thumb|center|&#039;&#039;16x16 Lattice at 1K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_0.01K_metastab.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 0.01K - takes ~8000 steps&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_at_4K.png|300px|thumb|center|&#039;&#039;32x32 Lattice at 4K - takes ~2000 steps, but never fully equilibrates&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Instead of choosing an arbitrary figure for number of points to discard, a method was added to the code to locate the point of equilibrium and reject all points before. &lt;br /&gt;
&lt;br /&gt;
For points where the lattice reaches the fully aligned state (lowest energy), this was easy to implement as the point when the fully aligned state is reached can be used as the point of equilibration. The code is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
For points where the lattice equilibrates but not at a fully aligned state, a different method was needed. A running list of energies was generated, and two points 10,000 steps apart were compared. If the difference in energy was less ta certain value (determined by experimentation), the system was supposed to have equilibrated.&lt;br /&gt;
&lt;br /&gt;
[[File:Not_min_energy_equil_code.PNG|1000px|thumb|center|&#039;&#039;Method for ensuring average is correct when the minimum energy state is not achieved&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
The statistics function was updated to return the correct average for these cases, and an extra case was added where the system never equilibrated according to either method above- the first 25,000 steps were ignored, as all systems were equilibrated long before this point.&lt;br /&gt;
&lt;br /&gt;
[[File:Updates_statistics_correct_AV.PNG|1000px|thumb|center|&#039;&#039;Updated statistics function&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your intuition 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. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simulations of 100,000 cycles were run every 0.01K from 0.25 to 5 K on an 8x8 Lattice. The plots of energy per spin vs temperature, and magnetisation per spin vs temperature, both with errorbars, are shown below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plots were created in Jupyter Notebook, using the code below.&lt;br /&gt;
&lt;br /&gt;
[[File:How_to_plot_graphs_script.PNG|thumb|800px|center|&#039;&#039;Script used to import data, calculate errors, plot magnetisation and energy data with errorbars, and save figures&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:Energy_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_2x2.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 2x2 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_4x4.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 4x4 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_8x8.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for an 8x8 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_16x16.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 16x16 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Energy_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Energy (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|[[File:Mag_vs_t_32x32.png|thumb|450px|center|&#039;&#039;Magnetisation (per spin) vs Temperature (K) for a 32x32 lattice, with errorbars&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As lattice size gets larger, the error in the data gets smaller. I would say a 16x16 lattice is large enough to capture long range fluctuations. At the curie temperature, the energy should change almost instantaneously from -2 to the higher energy. The 16x16 lattice is when the energy curve begins to match this enough to be a reasonable approximation.&lt;br /&gt;
&lt;br /&gt;
==Determining the heat capacity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: By definition,&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From this, show that&lt;br /&gt;
&lt;br /&gt;
C = &amp;lt;math&amp;gt;\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 E.)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; C = \frac{\partial \left\langle E\right\rangle}{\partial T} = \frac{\partial \left\langle E\right\rangle}{\partial \beta}\frac{\partial \beta}{\partial T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As &amp;lt;math&amp;gt; \beta = \frac{1}{k_b T} &amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\frac{\partial \beta}{\partial T} = \frac{-1}{k_b T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 — 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Plotting_heat_capacity_script.PNG|500px|center|thumb|&#039;&#039;Script for plotting heat capacity vs temperature&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Heat_capacity_peak_all_latt.png|500px|center|thumb|&#039;&#039;Heat Capacity vs Temperature for all lattice sizes&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==Locating the Curie temperature==&lt;br /&gt;
&lt;br /&gt;
For an infinite lattice size, the heat capacity diverges at the Curie temperature. However, with a finite lattice size the Curie temperature can be seen as a strong peak in heat capacity. The Curie temperature also changes with lattice size (finite size effect).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_en.png|500px|thumb|center|&#039;&#039;My Energy Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|[[File:C_dat_and_my_dat_32_mag.png|500px|thumb|center|&#039;&#039;My Magnetisation Data plotted against the C++ data for a 32x32 lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It can be seen that the data from C++ closely matches the data from my simulation, however the C++ data for energy is a smoother curve- due to more montecarlo steps resulting in less error in average energy. The C++ data for magnetisation shows earlier divergence from permanent magnetisation. The script used to plot this is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_32_my_dat_and_c_dat.PNG|800px|thumb|center|&#039;&#039;Code for plotting my data vs C++ data for the 32x32 lattice&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
===Polynomial Fitting===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code used for fitting a polynomial (order 30) to the heat capacity data for a 16x16 lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_plotting_c_vs_t_with_bad_fit.PNG|800px|thumb|center|&#039;&#039;Code used to plot C vs T with a fitted polynomial for the C++ data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit using this method. Smaller lattice sizes are relatively easy to fit, but lattice sizes over 4x4 become almost impossible to fit well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_intialfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a good fit with polynomial order = 15&#039;&#039;]]&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_bad_fit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a bad fit with polynomial order = 30&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_badfit.png|350px|thumb|center|&#039;&#039;Heat capacity data for a 4x4 lattice fitted- a very bad fit with polynomial order 30&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Fitting in a particular temperature range&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The selection of data within the array that corresponded to each peak was selected by trial and error. The code for fitting only the peak and plotting the result for the 32x32 Lattice is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Code_for_fitting_only_peak.PNG|800px|thumb|center|&#039;&#039;Code for fitting the peak of the 32x32 Lattice Heat Capacity data to a polynomial of order 30&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Restricting to the peak region made it far easier to get a good fit, as shown below.&lt;br /&gt;
&lt;br /&gt;
{|class = &amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left: auto; margin-right: auto; border: none;&amp;quot;&lt;br /&gt;
|[[File:2x2_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 2x2 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:4x4_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 4x4 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:8x8_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 8x8 Lattice&#039;&#039;]]&lt;br /&gt;
|-&lt;br /&gt;
|[[File:16x16_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 16x16 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:32x32_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 32x32 Lattice&#039;&#039;]]&lt;br /&gt;
|[[File:64x64_lattice_heatcap_C++dat_peak_fit.png|350px|thumb|center|&#039;&#039;Peak fit for 64x64 Lattice&#039;&#039;]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;i&amp;gt;Finding the peak in C&amp;lt;/i&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;TASK: 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 T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. 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.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The temperature at which the maximum in C occurred, along with the corresponding lattice size, was recorded in the datafile [[Media:Curie_for_diff_lattice_sizes.txt|&#039;&#039;Curie Temperature for different lattice sizes&#039;&#039;]] linked here. &lt;br /&gt;
&lt;br /&gt;
The scaling relation for Curie temperature is  &amp;lt;math&amp;gt;T_{C, L} = \frac{A}{L} + T_{C,\infty}&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;T_{C, L}&amp;lt;/math&amp;gt; is the Curie temperature of an &amp;lt;math&amp;gt;L\times L&amp;lt;/math&amp;gt; lattice, &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is the Curie temperature of an infinite lattice, and A is a constant.&lt;br /&gt;
&lt;br /&gt;
A curvefit was used to fit the data above to this scaling relation. The code for carrying out the fit and plotting the fit against the data is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temperatures_lattice_sizes_code.PNG|800px|thumb|center|&#039;&#039;Code for fitting the data to the relation given&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[File:Curie_temp_for_different_lattice_sizes_curvefit_correct.png|800px|thumb|center|&#039;&#039;Data for Curie Temperature vs Lattice size, plotted alongside the Curve_fit for this data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
From the curvefit, the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; can be extracted and is found to equal 2.279 K (4 s.f.). The theoretical exact curie temperature for the infinite 2D Ising Lattice has been calculated to be equal to 2.269 K (4 s.f.)&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;. My calculated value is surprisingly close to the theoretical exact value, considering the scaling relation plot has only been generated using 6 datapoints. From observing this plot, it is clear that the datapoints do not lie exactly along the curvefit plotted, and inaccuracy in the curvefit is likely a large source of error. There are minor errors throughout in peak fitting and variations in energy from montecarlo, which may result in datapoints not lying exactly on the correct relation curve, however with more datapoints (i.e. more lattice sizes) the relation curve could be more accurately generated and thus the value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated more accurately.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Square-lattice_Ising_model?fbclid=IwAR1-_iHwseqqxZUV9Cz57tKNsWxniKW-LbzruaI3rQDBLsS1HvxGD49weMQ|1] Exact 2D Ising Lattice Curie Temperature&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
&lt;br /&gt;
===Data Files===&lt;br /&gt;
&lt;br /&gt;
[[Media:2x2_final_dat_IM.dat|&#039;&#039;2x2 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:4x4_final_IM.dat|&#039;&#039;4x4 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:8x8_final_dat_IM.dat|&#039;&#039;8x8 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:16x16_final_dat_IM.dat|&#039;&#039;16x16 data&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
[[Media:32x32_final_dat_IM.dat|&#039;&#039;32x32 data&#039;&#039;]]&lt;/div&gt;</summary>
		<author><name>Im915</name></author>
	</entry>
</feed>